目前编程很少涉及到装饰器的编写或者使用,简单做一个笔记,方便日后回顾。
- 装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。
- 但是要想使用或者深入理解装饰器的工作逻辑,必须理解闭包这一概念。
1. 装饰器在被装饰的函数定义之后立即运行
1 | registry = [] |
所以上述代码,在main方法执行之前,装饰器装饰函数时就已经执行了。
如果将上述代码作为模块进行导入,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。
2. 一个经典的错例子
Python 编译函数的定义体时,它判断 b 是局部变量,因为在函数中给它赋值了。生成的字节码证实了这种判断,Python 会尝试从本地环境获取 b。后面调用 f2(3)
时, f2 的定义体会获取并打印局部变量 a 的值,但是尝试获取局部变量 b 的值时,发现 b 没有 绑定值。
将b正确作为全局变量的使用方法应该如下1
2
3
4
5
6b = 5
def f2(a):
global b
print(a)
print(b)
b = 9
3. 闭包
上述问题引出了一个关于变量作用域的问题,而闭包的概念也与变量的作用域有关。
在博客圈,人们有时会把闭包和匿名函数弄混。这是有历史原因的:在函数内部定义函数 不常见,直到开始使用匿名函数才会这样做。而且,只有涉及嵌套函数时才有闭包问题。 因此,很多人是同时知道这两个概念的
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。函数是不是匿名的没有关系,关键是它能访问定义体之外定义的非全局变量。
看下面这个例子,很好的帮助我们理解闭包这一概念:
首先是如下make_averager()
函数,其中自由变量series
在averager()
也可以访问。根据下面代码可以发现自由变量series
实际上是保存在了averager
对象的__closure__
属性中,在__code__.co_freevars
有保存其变量名。
- 提示:在Python中,函数也是一等对象
- 一等对象具有如下特性
- 在运行时创建
- 能赋值给变量或者数据结构中的元素
- 能做为参数传递给函数
- 能做为函数的返回结果
1 | def make_averager(): |
下述例子,关于nonlocal关键字:1
2
3
4
5
6
7
8
9
10def make_averager():
count = 0
total = 0
def averager(v):
# nonlocal 声明
nonlocal count, total
count += 1
total += v
return total / count
return averager
最后简单写一个计时的装饰器,同时记录递归深度。
1 | import functools |