学到装饰器,然后总结一下理解装饰器需要的知识
一.python的作用域
当然,Python与大多数编程语言一样,搜索变量值的时候,即命名空间的规则,会采用'就近原则'.
- 具体来说,由近及远依次为: 本地作用域(Local) --> 外部嵌套函数作用域(Enclosing Local) --> 全局/模块作用域(Global) --> 内置作用域(Built-in).
a
- Local 与 Enclosing 是一个相对的概念. 在函数 f1 中, a 是一个 Local 变量, 而在 f2 中 a 是一个 Enclosing 变量.
- 只有模块、类、函数才会引入新的作用域. 而 if for while 语句不会引入新的作用域.
- 全局作用域中的变量对于下层作用域比如函数来说, 是一个只读变量.
a
函数内部不是修改全局变量 a 的值,而是重新定义了一个本地变量 a.所以全局变量 a 的值没有改变
a = 4
def foo():
a = a + 3
foo()
# local variable 'a' referenced before assignment
在函数 foo 内部, a = a + 3 这个表达式的存在会让 Python 编译函数的定义体时,它判断 a 是局部变量,因为在函数中给它赋值了。
- 内部作用域中要修改外部作用域变量的值时,要用 global、nonlocal 关键字声明外部作用域变量
a
二.闭包与自由变量
什么是闭包?
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。函数是不是匿名的没有关系,关键是它能访问定义体之外定义的非全局变量
总结:闭包是个函数,它能够访问函数体之外定义的非全局变量,而这个非全局变量指的就是自由变量.
下面举一个例子, 定义一个 avg 函数,参数为一个值, 不断累加的计算从开始到现在所接收的全部值的平均值
def
说明:1调用 make_average 返回一个 average 函数对象.这就是一个闭包函数,因为 avg 可以访问 average 函数定义体之外的 series .
注意:这里嵌套函数 average 并没有'改变' series, 只是修改它的值, 因为 series 是一个可变的列表.所以并不会报错.那么如果 series 是一个不可变对象呢? 会发生什么?
上面的例子效率比较低, 没一次都得 sum.我们难道不可以保存每一步计算的 total 吗?
def
说明: 在嵌套函数 average 内部有 count += 1 ,此表达式等价为 count = count + 1.在编译阶段, 会把内部的 count 解释为一个本地变量, 所以如果没有 nonlocal 声明的话, 会报错 local variable 'count' referenced before assignment.
简单的处理当然不行, 我们需要使用 nonlocal 将 count 和 total 变成自由变量
def
三.装饰器
装饰器用来'装饰'一个函数,为函数添加额外的功能,一般来说并不是核心功能.
装饰器接收一个函数作为参数,装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
举例, 我们定义一个装饰器,用来计算并且显示每个函数运行的时间
import
让我们装饰一下别的函数
@decorate
`@decorate `是一个语法糖, 等同于 func = decorate(func), 所以此时 func 是 wrapper 函数的引用.
如何证明?
foo
这是一个瑕疵啊,我们需要改进.由此我们需要使用一些标准库装饰器.
使用 functools.wraps 装饰器把相关的属性(func._name 和 func.doc_)从 func 复制到 wrapper 中
from