创建闭包的要求
在python中创建一个闭包一般有三个要求:
(1)闭包函数必须有内嵌函数。
(2)内嵌函数必须要引用外层函数的变量。
(3)闭包函数返回的必须是内嵌函数的引用地址。
def func():
name='李四'
def inner():
print(name)
# print(inner.__closure__)
return inner
f=func()
f()
'''
运行结果:
李四
'''
闭包函数与普通函数有什么不同呢?
我们先假设没有return inner这句话:当调用func()函数的时候为值’李四’开辟一片内存空间,并分配了内存地址,将’李四’的内存地址赋值给name。因为没有调用inner()函数,所以不会执行inner()函数,执行完函数,进行垃圾回收。但是当return 了inner的内存地址就会调用inner()函数打印里面的内容。这就是闭包函数。
闭包
我们可以提几个问题?
问题1:
如何判断是不是闭包函数?
使用closure()方法
方法用途:
判断是不是闭包函数,是则返回带cell元素,不是则返回None。
代码示例:
def func():
name='李四'
def inner():
print(name)
print("判断是不是闭包函数,是则返回带cell元素,不是则返回None")
print(inner.__closure__)
return inner
f=func()
f()
'''
运行结果:
判断是不是闭包函数,是则返回带cell元素,不是则返回None
(<cell at 0x000001C3D31CA528: str object at 0x000001C3D3202818>,)
李四
'''
问题二:
最后两行代码为什么不能用func()直接代替?
这是因为执行return的时候返回一个内存地址,大家都知道,要调用一个函数,只需要内存地址是不够的,还得加上()所以还可以这样写。
代码示例:
def func():
name='李四'
def inner():
print(name)
return inner
func()()
'''
运行时间:
李四
'''
如果直接是return 函数名那么就会如下,返回的是inner()函数的内存地址,那么打印的也内存地址,并没有调用inner()函数!
代码示例:
def func():
name='李四'
def inner():
print(name)
return inner
print(func())
'''
运行结果:
<function func.<locals>.inner at 0x0000027DB07838C8>
'''
延迟绑定
不得不提的一道题:
题目:问如下程序打印的是什么?
def fun():
temp = [lambda x:i*x for i in range(4)]
return temp
for i in fun():
print(i(2),end=" ")
'''
运行结果;
6 6 6 6
'''
分析:首先调用fun()函数,在列表中将匿名函数循环了4次,所以列表中记录了匿名函数的四次内存地址,注意:每次匿名函数的内存地址都是不一样的!并返回给调用者,使用for i in 进行遍历列表中的内存地址,调用列表中四个匿名函数的内存地址,并且将2对匿名函数进行传参,在匿名函数的局部变量域中找不到i,然后就会扩大范围到上一层去找,在fun()函数中找到了,是3,你会问为什么是3呢,因为循环早已进行完毕。所以每次调用匿名函数所用的i是同一个,皆为3,这也导致运行结果是6 6 6 6 因为2乘3等于6嘛,这便是延迟绑定。
初学小白可能遇到如下问题?
第一:
没有搞清楚这是如何将x值传进去的?
解答:你可能会说这个2怎么能传给fun()函数啊,这个函数并没有定义形参来接受,那你就大错特错了,2并不是传给fun()函数,而传的是列表中的匿名函数。
第二:
为什么不是0,2,4,6?
调用匿名函数传参是在for i in range(4)之后进行的,并且在匿名函数的域中找不到i,才到外层找的i,这时的i已经是循环4次之后的i,就也是3。所以最后打印的全是6 6 6 6.
如果你还是不能很好的理解那么我可以把他改成一般形式,让你更好的理解!
代码示例:
def func():
list = []
for i in range(4):
def f(x):
return i * x
list.append(f)
return list
for i in func():
print(i(2),end=" ")
'''
运行结果:
6 6 6 6
'''
可以为参数设置默认值解决这个问题:
代码示例:
def fun():
temp = [lambda x,i=i:i*x for i in range(4)]
return temp
for i in fun():
print(i(2),end=" ")
'''
运行结果:
0 2 4 6
'''
或许还有很多方法,就不一一列举了,主要理解什么是延迟绑定,并怎么预防他。
来一波,推送吧!
群号:781121386
群名:人生苦短,我学编程
欢迎大家加入我们,一起交流技术!!!