一、嵌套函数
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
该链接例有 函数嵌套、函数使用、闭包的解释。
原文地址:https://www.cnblogs.com/jianguo221/p/8984618.html