@songying
2018-07-24T10:46:35.000000Z
字数 1462
阅读 1132
python高级特性
只有涉及嵌套函数时才有闭包问题。
objectname.__code__.co_freevars # 返回该对象中的自由变量名
闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
注意,只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量。
- 必须包含一个嵌套函数
- 嵌套函数必须引用封闭函数中定义的值(自由变量)
- 封闭函数必须返回嵌套函数
闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。
一般来说, 当对象中只有一个方法时,这时使用闭包是更好的选择,这比用类来实现更优雅。
__closure__
属性所有函数都有一个 __closure__
属性,如果这个函数是一个闭包的话,那么它返回的是一个由 cell 对象 组成的元组对象。cell 对象的cell_contents 属性就是闭包中的自由变量。
>>> adder.__closure__
>>> adder5.__closure__
(<cell at 0x103075910: int object at 0x7fd251604518>,)
>>> adder5.__closure__[0].cell_contents
5
这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因的,因为它存储在了闭包的 cell_contents中了。
上面的例子中自由变量Series是一个可变对象列表,当我们在闭包中修改Series时,解释器依旧会认为Series是一个自由变量而非局部变量。
但是当我们外部自由变量是数字,元组,字符串等不可变类型的时候,由于不可变性,我们在闭包中修改这类自由变量的时候,如果尝试重新绑定,如下面的count = count + 1, 其实会隐式创建局部变量 count。这样,count 就不是 自由变量了,因此不会保存在闭包中。
def make_averager():
count = 0
total = 0
def averager(new_value):
count += 1
total += new_value
return total / count
return averager
>>> avg = make_averager()
>>> avg(10)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'count' referenced before assignment
>>>
因此,针对第二种情况, python引入nonlocal声明,它的作用是把变量标记为自由变量,即使在函数中为变量赋予新值了,也会变成自由变量。如果为 nonlocal 声明的变量赋予新值,闭包中保存的绑定会更新。
正确的方式如下:
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count, total
count += 1
total += new_value
return total / count
return averager