十八、十九穿插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 对面老李

上面的代码有下面几个注意点:

  1. “greeting”函数访问了non-local的变量”prefix”,根据前面namespace的介绍,这点是完全合法的
  2. 变量”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

该链接例有 函数嵌套、函数使用、闭包的解释。

 

posted @ 2018-05-03 11:44  遥望那月  阅读(443)  评论(0编辑  收藏  举报