1. 什么是嵌套函数
嵌套函数就是在函数中定义函数,英文叫nested function
def outer(x): def inner(): print(x) inner()
这也很好理解,在函数outer
中定义了另外一个函数inner
,而inner
也必须在outer
中被调用才能执行。
我们还可以直接把内容定义的函数当做返回值:
def outer(x): def inner(): print(x) return inner()
这样outer
这个函数其实返回的是None
,它只是会打印出输入参数x的值
outer(10)>>> 10
当然,也有的地方直接返回函数名,而不是返回函数的执行结果:
def outer2(x): def inner(y): print(x+y) return inner
这是由于我们返回的是函数名,因此在使用的时候,后面需要跟参数:
foo = outer2(10)foo(5)>>> 15
2. 变量作用域
两个函数处于不同的层次,肯定会有作用域的问题,关于Python中的作用域,网上的文章一大堆,这里我们说一下初学者们可能会忽略的东西。Python的函数中是不能对全局变量进行赋值,只能读取全局变量的值,但list
和dict
除外,这是因为list和dict都是可变类型,而Number、String、Tuple和Sets这些都是不可变类型。
a = 1def inc(): a += 1 return ainc()
执行这段代码就会提示:
UnboundLocalError: local variable 'a' referenced before assignment
但如果我们在函数内部把全局变量a的值打印出来,不去对它进行写操作:
a = 1def prt(): print(a)prt()
这段代码是可以正确执行的。
如果我们就是想在函数中修改全局变量的值,可以使用
global
关键字:a = 1def inc(): global a a += 1 return ainc()
如果在函数中重新定义一个名字一样的变量:
a = 1def inc(): a = 2 return aprint(inc())print(a)>>> 2>>> 1
这样的话,函数内部定义的变量a只是个局部变量,它是不会影响到全局变量a的。
如果我们操作的是list:
a = [1]def inc(): a[0] += 1 return a[0]inc()
这段代码是可以执行的,我们也可以为list中添加其他元素:
a = [1]def inc(): a.append(2) return ainc()
如果重新定义相同名字的list,也只会是局部变量,不影响全局的list:
a = [1]def inc(): a = [2,3] return aprint(inc())print(a)>>> [2, 3]>>> [1]
嵌套函数的变量作用域跟上面所讲的基本是一致的,只是如果内部的函数想使用外部函数的变量,需要使用关键字
nonlocal
def outer(): a = 10 def inner(): nonlocal a a += 1 print(a) inner()outer()>>> 11
3. 闭包
说到嵌套函数,就必须要讲闭包,英文是Closures,什么是闭包?百度百科的解释如下:
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
如何创建闭包?需要满足下面三点:
闭包函数必须有内嵌函数
内嵌函数需要引用该嵌套函数上一级中的变量
闭包函数必须返回内嵌函数
所以我们在上面定义的outer2
中的inner()
,就会形成一个闭包。我们把前面的例子在展示一遍:
def outer2(x): def inner(y): print(x+y) return innerfoo = outer2(10)foo(5)>>> 15
为什么叫闭包呢?因为即便我们把outer2删除调,再执行foo()也是有效的,不会因为离开它的执行空间而被销毁导致无迹可寻。
del outer2foo(6)>>> 16
闭包函数相对与普通函数会多出一个__closure__
的属性,里面定义了一个元组用于存放所有的cell
对象,每个cell
对象一一保存了这个闭包中所有的外部变量。
foo.__closure__>>> (<cell at 0x0000000004FE4408: int object at 0x00000000734D6D60>,)foo.__closure__[0].cell_contents >>> 10
其他函数也有__closure__
属性,只不过都是None。
4. 什么时候使用闭包
闭包可以避免使用全局值,并提供某种形式的数据隐藏,也提供了一种面向对象的解决方案。
当一个类只包含一个方法,此时比较适合使用闭包。但如果类中包含的方法比较多,还是直接使用class来定义比较合适。
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier
闭包的使用:
# Multiplier of 3times3 = make_multiplier_of(3)# Multiplier of 5times5 = make_multiplier_of(5)
Python高性能系列文章
欢迎关注微信公众号:Quant_Times
本文分享自微信公众号 - 科学计算Tech(Quant_Times)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。