装饰器基础
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。
装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时 (即 Python 加载模块时)
变量作用域规则
对比代码
python
b = 3 |
python
b = 3 |
这不是缺陷,而是设计选择:Python 不要求声明变量,但是假定在函数定义体中赋值的变量是局部变量。
如果在函数中赋值时想让解释器把 b 当成全局变量,要使用 global 声明:
python
b = 3 |
闭包
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。
假如有个名为 avg 的函数,它的作用是计算不断增加的系列值的均值。
python
10) avg( |
那么它的实现,可以是类的形式:
python
class Average: |
也可以是高阶函数
python
def make_averager(): |
注意nonlocal total, count
:
因为在Python中并没有要求先声明一个变量,所以Python解释器任务在函数体内,只要对一个变量进行赋值操作,那么这个变量就是局部变量。而 count+=1相当于 count=count+1,对 count 进行了赋值操作,所以Python解释器认为 count 是函数内的局部变量。我们这里需要用nonlocal关键字将局部变量修正为自由变量。
所谓自由变量就是没有被绑定在局部作用域的变量。
装饰器
利用闭包实现日志装饰器:对于被装饰函数,每次调用打印调度log
python
def logit(func): |
@符号是一个语法糖,实际的执行是:logit(some_func(x))
如果装饰器需要带参数,(例如下面的代码实现了将日志保存到指定文件),则需要再加一层:
python
def logit(log_file = 'out.log'): |
Ref
《流畅的Python》
More
关于装饰器更多内容还包括:
- [ ] @functools.wraps
- [ ] @lru_cache()和@singledispatch
- [ ] 类装饰器