Python 基础:闭包(Closure) 介绍

闭包 (Closure)

<<流畅的Python>>中对闭包的定义:

闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量,它能访问定义体之外定义的非全局变量。

闭包有效的减少了函数所需定义的参数数目

闭包 与 lambda

闭包 与 lambda 同时使用时,注意作用域的问题

1. 如下代码:

def createlist():
	# [x... for x in xx if x...]
	# 列表推导,从现有列表获取一个新的列表
	return [lambda x: i*x for i in range(5)]

# createlist() 返回一个各元素是lambda表达式的列表
for i in createlist():
	print(i(2))

# 输出:
8
8
8
8
8

createlist函数返回值的列表,每一个元素都是一个函数,指定倍数获取新列表,预期是:[0,2,4,6,8]

但是其中有一个容易忽略的陷阱,Python中的属性查找规则: LEGB(local,enclousing,global,bulitin),在上面的例子中,i 就是在闭包作用域(enclousing)。

而 Python 的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的。

createlist() 被调用后,列表个元素都是匿名函数,匿名函数使用了闭包中的变量i,而此时i的值是4,所以对列表个元素求值的结果都是8

所以正确写法是:

def createlist():
	# 在匿名函数中定义一个局部变量保存自己需要的值
	return [lambda x,i=i: x*i for i in range(5)]

for i in createlist():
	print(i(2))

# 或 另一种写法

def createlist():
	a=[]
	for i in range(5):
		def innner(x, i=i):
			# 为什么 i=i 定义在函数内会报错???
			# 代码块中变量第一次赋值和调用同时出现无法赋值
			# i = i
			return x*i
		a.append(innner)
	return a

for i in createlist():
	print(i(2))

2. 如下代码:

nums = range(2,20)
for i in nums:
    nums = filter(lambda x: x==i or x%i, nums)

print(list(nums))

# 输出:
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

同样按照正常的逻辑结果应该为:[2, 3, 5, 7, 11, 13, 17, 19]

问题产生的原因:

  • 在 python3 当中 filter() 函数返回的是一个迭代器,因此并没有做真正的执行,而是在每次调用的时候执行
  • python2 中 filter() 返回的值列表,无此现象
  • 在遍历后执行打印时,i这个变量使用的是最后调用时的值

正确代码:

nums = range(2,20)
for i in nums:
    nums = filter(lambda x,i=i: x==i or x%i, nums)

print(list(nums))

# 输出:
[2, 3, 5, 7, 11, 13, 17, 19]

# Python  Closure 

作者:ITmob
来源:ITmob.cn
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×