十八十九穿插python沉淀之路--嵌套闭包递归,三者的区别
Posted 遥望那月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十八十九穿插python沉淀之路--嵌套闭包递归,三者的区别相关的知识,希望对你有一定的参考价值。
一、嵌套函数
python允许在定义函数的时候,其函数体内又包含另外一个函数的完整定义,这就是我们通常所说的嵌套定义。为什么?因为函数是用def语句定义的,凡是其他语句可以出现的地方,def语句同样可以出现。
像这样定义在其他函数内的函数叫做内部函数,内部函数所在的函数叫做外部函数。当然,我们可以多层嵌套,这样的话,除了最外层和最内层的函数之外,其它函数既是外部函数又是内部函数。
定义:简单点儿理解就是函数(包括定义函数)内部定义了新函数,即一层套一层。
¥¥¥¥¥嵌套函数里面两个重要的概念:变量作用域 和 函数闭包
例1
1 # 例1 2 a = 123 3 def outer(): 4 def inner(): 5 a = 44 6 print(\'current a:\',a) 7 return inner 8 outer() #这一步只会访问到 outer() 这一层,而这一层什么都没有打印 9 in0 = outer() #通过这种方式才能访问到里层函数inner() 10 in0()
1 current a: 44
例2 :续例1 的继续深化解释
1 # 例2 2 name = \'China\' 3 def outer(): 4 name = \'Beijing\' 5 def middle(): 6 name = \'海淀\' 7 def inner(): 8 name = \'中关村\' 9 print(\'inner层的name:\',name) 10 inner() 11 print(\'midlle层的name:\',name) 12 middle() 13 print(\'outer层的name:\',name) 14 15 outer() 16 print(\'最外层的name:\',name)
1 inner层的name: 中关村 2 midlle层的name: 海淀 3 outer层的name: Beijing 4 最外层的name: China
之所以通过一个outerI()就打印出了所有,是因为都加了print,下一个例子将去掉print,来验证如何访问到内层的函数
例3:续例2
1 name = \'China\' 2 def outer(): 3 name = \'Beijing\' 4 def middle(): 5 name = \'海淀\' 6 def inner(): 7 name = \'中关村\' 8 print(\'inner层的name:\',name) 9 inner() #调用inner(),让其运行。 10 # print(\'midlle层的name:\',name) 11 middle() #调用middle() 让其运行 12 # print(\'outer层的name:\',name) 13 14 outer() 15 print(\'最外层的name:\',name)
1 inner层的name: 中关村 2 最外层的name: China
依结果可知:调用最外层函数,可以把最内部的inner调用打印出来。这是由于内部每一层都实现了调用。
二、闭包函数
1、何为闭包?
“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
通俗的讲:就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。
自解:闭包:一个函数里面嵌套一个函数的时候,调用外层函数返回里层函数本身
例1
1 def fx(x): 2 x+=1 3 def fy(y): 4 return x*y 5 return fy # 这个语句不可以换成 fy(y) 6 7 f = fx(4) 8 n = f(4) 9 print(n)
例2
1 def fx(x): 2 x+=1 3 def fy(y): 4 print(x*y) 5 return fy # 这个语句不可以换成 fy(y) 6 7 f = fx(4) 8 f(4)
例1、例2 的输出结果都是 20.。。print和return的差别,导致外面调用时的形式也有所不同。
2、举例解析
1 def greeting_conf(prefix): 2 def greeting(name): 3 print(prefix,name) 4 return greeting 5 6 mgreeting = greeting_conf(\'Good Morning\') 7 mgreeting(\'隔壁老王\') 8 mgreeting(\'对面老李\') 9 10 agreeting = greeting_conf(\'Good Night\') 11 agreeting(\'隔壁老王\') 12 agreeting(\'对面老李\')
1 Good Morning 隔壁老王 2 Good Morning 对面老李 3 Good Night 隔壁老王 4 Good Night 对面老李
上面的代码有下面几个注意点:
- “greeting”函数访问了non-local的变量”prefix”,根据前面namespace的介绍,这点是完全合法的
- 变量”prefix”并没有随着函数的退出而销毁,反而是生命周期得到了延长
下面看看为什么变量”prefix”生命周期得到了延长?
__closure__属性
在Python中,函数对象有一个__closure__属性,我们可以通过这个属性看看闭包的一些细节
1 def greeting_conf(prefix): 2 def greeting(name): 3 print(prefix,name) 4 return greeting 5 6 mgreeting = greeting_conf(\'Good Morning\') 7 mgreeting(\'隔壁老王\') 8 mgreeting(\'对面老李\') 9 print(dir(mgreeting)) 10 print(mgreeting.__closure__) 11 print(type(mgreeting.__closure__[0])) 12 print(mgreeting.__closure__[0].cell_contents)
1 Good Morning 隔壁老王 2 Good Morning 对面老李 3 [\'__annotations__\', \'__call__\', \'__class__\', \'__closure__\', \'__code__\', \'__defaults__\', \'__delattr__\', \'__dict__\', \'__dir__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__get__\', \'__getattribute__\', \'__globals__\', \'__gt__\', \'__hash__\', \'__init__\', \'__init_subclass__\', \'__kwdefaults__\', \'__le__\', \'__lt__\', \'__module__\', \'__name__\', \'__ne__\', \'__new__\', \'__qualname__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\'] 4 (<cell at 0x00000173FC168468: str object at 0x00000173FC1FDAF0>,) 5 <class \'cell\'> 6 Good Morning
通过__closure__属性看到,它对应了一个tuple,tuple的内部包含了cell类型的对象。
对于这个例子,可以得到cell的值(内容)为”Good Morning”,也就是变量”prefix”的值
从这里可以看到闭包的原理,当内嵌函数引用了包含它的函数(enclosing function)中的变量后,这些变量会被保存在enclosing function的__closure__属性中,成为enclosing function本身的一部分;也就是说,这些变量的生命周期会和enclosing function一样。
闭包和函数
闭包只是在表现形式上跟函数类似,但实际上不是函数。
1 def greeting_conf(prefix): 2 def greeting(name): 3 print(prefix,name) 4 return greeting 5 6 mgreeting = greeting_conf(\'Good Morning\') 7 print(\'function name is :\',mgreeting.__name__) 8 print( \'id of mgreeting is :\',id(mgreeting)) 9 10 agreeting = greeting_conf(\'Good Night\') 11 print(\'function name is :\',agreeting.__name__) 12 print( \'id of agreeting is :\',id(agreeting))
1 function name is : greeting 2 id of mgreeting is : 2592360143456 3 function name is : greeting 4 id of agreeting is : 2592360143592
从代码的结果中可以看到,闭包在运行时可以有多个实例,不同的引用环境(这里就是prefix变量)和相同的函数(这里就是greeting)组合可以产生不同的实例。
Python中怎么创建闭包
在Python中创建一个闭包可以归结为以下三点:
- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级namespace中的变量
- 闭包函数必须返回内嵌函数
通过这三点,就可以创建一个闭包,,Python装饰器就是使用了闭包。
附注:https://www.cnblogs.com/baomanji/p/6701981.html
该链接例有 函数嵌套、函数使用、闭包的解释。
以上是关于十八十九穿插python沉淀之路--嵌套闭包递归,三者的区别的主要内容,如果未能解决你的问题,请参考以下文章