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

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

  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

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

 

以上是关于十八十九穿插python沉淀之路--嵌套闭包递归,三者的区别的主要内容,如果未能解决你的问题,请参考以下文章

九python沉淀之路--递归全局变量局部变量作用域

C1认证学习十八十九(表单元素转义字符)

C1认证学习十八十九(表单元素转义字符)

《浪潮之巅》十八十九章笔记

#yyds干货盘点# 前端歌谣的刷题之路-第八十八题-参数解析器

百万年薪python之路 -- 闭包