Python闭包

装饰器实际上就是对闭包的使用

Python里一个函数里面又定义了一个函数,内部函数却可以引用外部函数的参数,局部变量;而当外部函数执行完毕返回内部函数引用的时候,以上所有参数也一同保存在返回的内部函数当中,这样就构成一个闭包,也就是说,外部函数要返回的时候发现自己的局部变量内部函数还要用,那么本属于它的局部变量并没有释放,而是给内部函数引用,自己结束返回

def outside(a):
b = 1
def inside(c):
d = 3
return a + b + c + d
return inside

这里的外部函数outside带一个参数a,定义一个局部变量b赋值为1,没有执行任何步骤,里面定义了一个内部函数inside,参数c,局部变量d赋值为3,返回,外部函数返回

f = outside(0)
print(f(2))

outside传了个参数0,但是啥都没干就返回了inside,也就是内部函数的引用,这样inside函数引用就传递给了f这个变量,也就是说f和inside指向了同一个函数对象,与此同时a和b都已经绑定到了内部函数被inside引用,因此最后执行f(2)实际上就是执行inside(2),四个参数的和返回6

如果看得比较疑惑,可以打印一下,看f到底指向哪里

print(f.func_name)

下面有一个关于循环变量使用的问题

def outside(a):
b = 1
fs = []
for i in range(3):
def inside():
c = 2
print id(i)
return a + b + c + i
fs.append(inside)
return fs


f0, f1, f2 = outside(0)
print(f0(), f1(), f2())

这里返回的是5,5,5,而不是预期的3,4,5;原因是python在函数执行时才会查找函数体里变量的值,这里三个函数添加到fs的时候,i还没开始用,只有在执行f0()的时候再去查看i的值,这时候已经是2了,通过打印id可以看到三次执行i引用的是同一个对象

140559981263952
140559981263952
140559981263952
(5, 5, 5)

Process finished with exit code 0

因此返回值不要引用会发生变化的变量或者函数

改进的话:

def outside(a):
b = 1
fs = []
for i in range(3):
def inside(d):
c = 2
def g():
print id(d)
return a + b + c + d
return g
fs.append(inside(i))
return fs


f0, f1, f2 = outside(0)
print(f0(), f1(), f2())

这里如果需要确保f0,f1,f2分别绑定的是0,1,2来执行,可以在内部函数inside里再添加一个函数来返回结果,但是内部函数inside来将循环变量i作为参数传值进去给d,i可以随便变,但是copy进来作为形参的d,每次引用的是不同的对象,因此f0,f1,f2执行的时候,分别绑定的是不同值的变量,同样可以打印id来查看

140651769412736
140651769412712
140651769412688
(3, 4, 5)

Process finished with exit code 0

大致区别,前一种方式,函数执行的时候,绑定的都是i,值都为2;后一种方式,绑定的都是函数体里传进来的参数,不同的变量,值分别0,1,2

发表回复