Nonlocal variable in a nested function
nested function: 一个函数定义在顶一个函数内部
nested function 可以访问 enclosing scope(即包裹着它的更大的空间)中的变量,这些变量相对于它称之为 nonlocal variable。
Nonlocal variables 是只读的,如果想修改它们,必须使用 nonlocal 关键字。
在 Python 2 中对应 global
def print_msg(msg):
# This is the outer enclosing function
def printer():
# This is the nested function
print(msg)
printer()
# We execute the function
# Output: Hello
print_msg("Hello")
We can see that the nested function printer()
was able to access the non-local variable msg
of the enclosing function.
Defining a Closure Function
在上面的例子中,如果外部函数print_msg()
返回值是其内部函数printer
呢?
def print_msg(msg):
# This is the outer enclosing function
def printer():
# This is the nested function
print(msg)
return printer # this got changed
# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()
我们给print_msg()
函数传入了参数”Hello”,该函数返回了一个函数printer
,并赋值给了another
,那么another
就成了一个函数,可以用another()
来调用。
此时虽然print_msg()
已经执行完毕,但再执行another()
时,”Hello”这条消息仍然有效,“printer”这个内部“变量”也仍然有效,不会因为”离开”了其执行空间而被”销毁”导致无迹可寻。
这种技巧就叫做 Closure。
更进一步:
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined
即使print_msg
这个函数体被垃圾回收了,another
以及刚才的内部变量msg
仍然有效。
Closure的定义
下面三个条件都必须满足,才构成一个 Closure:
- 有一个nested function
- nested function 必须 refer to a value defined in the enclosing function.
- The enclosing function must return the nested function.
Closure 的用处
- Closure 可以减少 global value 的使用,并做到一定程度的 data hiding,提供一个 object oriented solution(面向对象的解决方案)
- 当一个类只包含一个方法,使用 Closure 会更加 elegant。
例如:
def make_multiplier_of(n):
e = 2
def multiplier(x):
return e * x * n
return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
# Output: 54
print(times3(9))
# Output: 30
print(times5(3))
# Output: 120
print(times5(times3(2)))
下一节的 Python Decorator 广泛的使用了 Closure。
一些补充
所有的函数都包含一个 __closure__
属性,如果该函数是一个 normal function,则该属性为None;如果该函数是一个 closure function,则该属性为 a tuple of cell objects。
根据上面的例子,times3
和times5
都是 closure function
>>> make_multiplier_of.__closure__
>>> times3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
有几个内部变量就对应几个 cell
>>> times3.__closure__[0].cell_contents
3
>>> times3.__closure__[1].cell_contents
2