python函数装饰器和闭包
目录
- 装饰器
- 装饰器定义
- 装饰器执行时间
- 变量作用域与global声明
- 闭包
- 闭包定义
- 闭包三个必要条件
- 闭包示例
- 闭包中自由变量储存位置
- nonlocal声明
- 标准库中的装饰器
- 内置装饰器
- 常用装饰器
装饰器
装饰器定义
- python装饰器本质就是一个高阶函数,其一般以一个函数为参数,返回另一个函数,返回值可以是对原函数的改造,也可以跟原函数毫无关系。如下两种代码效果完全等价:
@decorate def target(): print("running target()")
def target(): print("running target()") target = decorate(target)
- 装饰器一般都是在内部定义一个新函数再返回,而不是返回原函数
装饰器执行时间
- 在导入模块时运行装饰器,只要.py模块被加载进内存,装饰器函数立即运行。
- 装饰器一般在一个单独的模块中定义,然后应用到其它模块的函数上
变量作用域与global声明
- 重所周知,局部作用域能访问到全局变量,而局部变量在作用域外不能被访问。
- 下边例子中,局部作用域访问全局变量失败,提示局部变量在赋值前被引用,这是因为python假定在函数体内赋值的变量是局部变量。因此打印a的时候将a当成了局部变量,而a的赋值在打印语句下方,因此找不到a。
a = 7 def f(): print(a) a = 9 if __name__ == "__main__": f()
- 若想在函数体内赋值a,又想打印的时候把a当成全局变量,则需要使用global声明:
a = 7 def f(): global a print(a) a = 9 if __name__ == "__main__": f()
闭包
闭包定义
- 闭包指延伸了作用域的函数,延申作用域是指,其中包含了在函数定义体中引用,但是不在定义体中定义的非全局变量。
闭包三个必要条件
- 必须嵌套函数
- 内部函数引用在定义体外,但是在外部函数定义体内定义的变量,称之为自由变量。
- 外部函数的返回值是内部函数,此时内部函数作用域延申,会把自由变量保存下来。即外部函数返回后自由变量没有回收,而是留到了闭包中被返回。
闭包示例
- 问题描述:
-
计算移动平均值的高阶函数
-
该例子中在内部函数averager中引用了在外部函数定义体中定义的变量series,series即为自由变量,averager加上series即形成了闭包,当外部函数make_averager返回时,自由变量series并未被回收,而是随着闭包一同返回。
-
闭包中自由变量储存位置
- 如上边例子,设返回的函数对象为avg,则avg.__code__.co_varnames保存函数的局部变量名称,avg.__code__.co_freevars保存自由变量的名称
- 真正的自由变量储存在avg.__closure__属性中。
- avg.__closure__[0].cell_contents中保存着真正的自由变量的值
nonlocal声明
- 求移动平均值更合适的思路应该是只保存当前的总值和元素个数。
- 尝试使用函数make_averager时:
- 会提示找不到局部变量,这是因为count是不可变类型,count += 1 等价于 count = count + 1,并非是原地操作,因此count已经变成了内部函数averager的一个局部变量,而不再是自由变量,因此不会被保存到闭包中。
- python3引入nonlocal声明,作用是将变量标记为自由变量,如果为nonlocal声明的变量赋予新值,则闭包中保存的值也会更新。
标准库中的装饰器
内置装饰器
- property, classmethod, staticmethod
常用装饰器
- functools.wraps, functools.lru_cache,functools.singledispatch