Python3入门与进阶——““面向对象,”闭包”。(也没有很难)

一:一切皆对象。

Python一切都是“对象”,包括“函数”。在很多其它语言中,函数只是一段可执行代码,但Python的“函数”是可以实例化的。

因此可以做到:

a=def

也可以:把函数作为对象传递到另一个函数里作为参数,也可以把一个函数作为另一个函数的返回结果。


def curve_pre():
    def curve():
        pass
    return curve#返回一个函数
    
f=curve_pre()#函数赋给一个变量


二:什么是闭包?

首先需要知道,当需要用到一个变量值,但是在本级作用域没有定义,它就会在上一级作用域中去找。(普遍是这样)

闭包=函数+环境变量(内嵌函数定义时候的外部变量,但又不能是全局变量)

例子:
def curve_pre():
	a=25
	def curve(x):
		return a*x*x
	return curve
	
a=10
f=curve_pre()
print(f(2))
#f现在就是curve函数,但是它没有a的值,如果从上一级中去寻找,那应该打印40,但闭包会从“环境变量“中去找a的值。a=25就是闭包的环境变量。


结果:
100

  大致明白了”环境变量“和“闭包”的意思了。

  闭包只是在表现形式上跟函数类似,但实际上不是函数。

  一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数  对象。环境变量取值被保存在函数对象的__closure__属性中。__closure__里包含了一个元组(tuple)。这个元组中的每个元素是cell类型的对象。

   


def curve_pre():         
    a=25
    def curve(x):
        return a*x*x
    return curve
    
def curve_pre():
    a=30
    def curve(x):
        return a*x*x
    return curve#因为环境变量不同,这里是两个不同的闭包:要关注整体,除了看函数还要看环境变量。


以下是“闭包”的经典误区:


def f1():
    a=10
    def f2():
        a=20#这个a是一个局部变量,不会影响到上面的a。
        print(a)
    print(a)
    f2()
    print(a)
    
f1()


思考一下上面代码的输出结果是什么?

结果:


10
20
10


再看一下下面这段代码:


def f1():
    a=10
    def f2():
        a=20
        return a
    return f2
f=f1()
print(f)
print(f.__closure__)


结果:


<function f1.<locals>.f2 at 0x00DAA100>
None


我们发现f,即函数f1的返回,即f2,不是一个“闭包”。

f2和上面curve函数不同的地方在于f2也定义有a变量,是这个的原因吗?

我们实践一下就知道了:


def f1():
    a=10
    def f2():
        #a=20
        return a
    return f2
f=f1()
print(f)
print(f.__closure__)


结果:


<function f1.<locals>.f2 at 0x00F4A100>
(<cell at 0x00F3A1F0: int object at 0x6A09E840>,)


激动人心!!现在f2是一个闭包了!__closure__属性再次回到它的身边。

然而,删掉f2内部对a的定义f2又成为一个闭包只是表面想象,实质的过程是:在f2内部有a的情况下,python认为a是一个局部变量,而不是一个环境变量,没有环境变量,闭包就不存在了;删掉a之后,a是一个环境变量了,闭包复活。

ps:查了一下资料,发现我对整体的观念还是不够深刻,在上面,我以为f2是闭包,但严格上来讲,f1才是一个闭包(从整体上考虑),也就是说,闭包包含:函数+内嵌函数+环境变量(被内嵌函数调用)

三:如何创建一个闭包?

在Python中创建一个闭包可以归结为以下三点:

  • 闭包函数必须有内嵌函数
  • 内嵌函数需要引用该嵌套函数上一级中的变量
  • 闭包函数必须返回内嵌函数

通过这三点,就可以创建一个闭包。(参考博客园Bluesky)

四:global与nonlocal

global 变量名#把该变量声明为全局变量(如果之前定义有同名的全局变量,那么这两个变量是同一个变量)

nonlocal 变量名#把该变量声明为“不是局部变量”,它会去找上一级变量。

五:闭包的环境变量意义

环境变量具有“保存现场”的功能,可以记忆上一次的状态

老师:“经常用全局变量是一件很糟糕的事。值被改变都不知道。”

六:解决“旅行者问题”的普通法和闭包法。

普通法:


origin=0
def function(new):
    global origin
#如果没有这行,会报“local variable 'origin' referenced before assignment”
    total=new+origin
    origin=total
    return total

print(function(2))
print(function(2))
print(function(2))


结果:


2
4
6


闭包法:


def function1(origin):
    def function2(new):
        nonlocal origin
#没有这行也会报“变量在声明之前就被引用的错误”
        total=origin+new
        origin=total
        return total
    return function2

f=function1(0)
print(f(2))
print(f(2))
print(f(2))


结果:


2
4
6


七:查看闭包的环境变量。

一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的__closure__属性中

闭包的函数的内置变量__closure__里包含了一个元组(tuple)。这个元组中的每个元素是cell类型的对象。


def function1(origin):
    def function2(new):
        nonlocal origin
        total=origin+new
        origin=total
        return total
    return function2

f=function1(0)
print(f.__closure__)
#打印环境变量(cell类型)1
print(f.__closure__[0].cell_contents)#打印环境变量(用我看得懂的阿拉伯数字)2

print(f(2))#这一步之后环境变量变为2了  3
print(f.__closure__)   4
print(f.__closure__[0].cell_contents)   5

print(f(2))   6
print(f.__closure__)   7
print(f.__closure__[0].cell_contents)   8

print(f(2))   9
print(f.__closure__)   10
print(f.__closure__[0].cell_contents)   11


结果:


(<cell at 0x00AFA178: int object at 0x68E9E7A0>,)    1
0   2
2   3
(<cell at 0x00AFA178: int object at 0x68E9E7C0>,)   4
2    5
4    6
(<cell at 0x00AFA178: int object at 0x68E9E7E0>,)    7
4   8
6   9
(<cell at 0x00AFA178: int object at 0x68E9E800>,)    10
6   11


可以发现,闭包的环境变量在个程序之中达到了”保存现场“的效果。(这个值可以保存下来,)

八:一些补充。

“我们可以看到,line定义的隶属程序块中引用了高层级的变量b,但b信息存在于line的定义之外 (b的定义并不在line的隶属程序块中)。我们称b为line的环境变量。事实上,line作为line_conf的返回值时,line中已经包括b的取值(尽管b并不隶属于line)

上面的代码将打印25,也就是说,line所参照的b值是函数对象定义时可供参考的b值,而不是使用时的b值。”(来源于博客园,忘记作者谁了,没有把源代码带过来,大致的意思就是环境变量是两个函数的夹心馅

我的一些小结:闭包类似两个函数加上夹在两个函数中间的”环境变量“。类似于三明治。但是外面那个函数又是包裹着里面那个内嵌的函数。闭包返回来的是里面那个函数。在使用过程之中,可以添加代码使”馅料“保持更新的状态。

九:七月老师对函数式编程的看法。

python强调闭包的环境变量。

闭包机制:一个函数(内嵌的)调用另一个函数的局部变量。

环境变量长驻内存,可能造成“内存泄露”。JavaScript使用闭包过多网页可能会卡顿。

函数式编程不一定好,看自己适不适合。

发布了46 篇原创文章 · 获赞 9 · 访问量 917

猜你喜欢

转载自blog.csdn.net/weixin_45850939/article/details/104450881