函数对象&闭包函数
函数对象
python的世界,一切皆对象,函数也不例外。
函数对象可以像普通变量那样被引用、被存放在容器数据中当元素,作为一个函数的实参、也可以作为函数的返回值。
函数被引用
函数名可以赋值给新的变量名,相当于起别名,别名加括号同样可以调用函数。
def add(x, y):
print(x+y)
func = add
func(3,4) # 等同于 add(x, y); 类似变量的赋值引用
函数做容器对象的元素
# 函数名相当于一个变量名,容器保存这个函数的内存地址,就类似列表内套一个列表对象
函数做实参
# 将函数做另一个函数的实参,相当于这个函数的返回结果作为另一个函数的实参
def func(x):
return x + 1
def add(x, func):
print(func(x))
add(3, func) # 打印:4
函数做返回值
def add(x, y):
return x+y
def foo():
return add
func = foo() # 相当于 func = add
func(3, 4) # x相当于 add(3, 4)
闭包函数
闭&包
基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。
也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)。
def outer(x):
def inner():
return x+1 # 此时 inner是闭包函数(引用了外层函数作用域的变量)
return inner
x = 10
def outer():
def inner():
return x+1 # 此时,inner不是闭包函数(没有引用外层函数作用域的变量)
return inner
判断闭包的方法
# 方法1:是否引用外层函数作用域中的变量
# 方法2:通过函数的closure属性,查看到闭包函数所包裹的外部变量。不是闭包该值为None
# func.__closure__ # 元组形式
# func.__closure__[0].cell_contents # 查看具体引用的第一个值
“闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。
因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。
闭包的用途
为函数体传值(传参)。
def outer(s):
def inner():
print(s)
return inner
func = outer('hello world')
func() # 打印: 'hello world'
func() # 打印: 'hello world'
一次传参,后续使用无须再传参 。闭包函数的这种特性有时又称为惰性计算。
变形装饰器(下一节再细讲)
补充
>>> def outer(x):
... def inner_reads():
... # Will return outer's 'x'.
... return x
... def inner_writes(y):
... # Will assign to a local 'x', not the outer 'x'
... x = y
... def inner_error(y):
... # Will produce an error: 'x' is local because of the assignment,
... # but we use it before it is assigned to.
... tmp = x
... x = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment
上述函数 inner_writes(10)无法修改变量x,是因为内修改的仅仅是inner_writes局部名称空间内的变量x,而inner_reads()度的还是外层函数outer的x。如果非要修改可以使用'nonlocal'声明x,但不能使用'global'。