我在Python中实现了两个简单的闭包。对我来说,它们看起来是一样的,但是一个有用,另一个没有。
工作的是:
def makeInc(x, y):
def inc():
return y + x
return inc
inc5 = makeInc(5, 10)
inc10 = makeInc(10, 5)
inc5 () # returns 15
inc10() # returns 15
但第二个不起作用:
import os
def linker(dest, filename):
print filename
def link():
if os.path.isfile(filename): # line 17
filename = os.path.join(os.getcwd(), filename)
dest = os.path.join(dest, filename)
y = rawinput('[y]/n: ln -sf %s %s' % (dest, filename))
if y == 'n':
return 1
else:
return os.system('ln -sf %s %s' %(dest, filename))
else:
return -1
return link
l = linker('~', '.vimrc')
l() # line 30
执行link()
时,l()
的第一行出错:
Traceback (most recent call last):
File "test.py", line 30, in <module>
l()
File "test.py", line 17, in link
if os.path.isfile(filename):
UnboundLocalError: local variable 'filename' referenced before assignment
他们看起来和我一模一样,所以我不明白为什么第二个人不能工作。有什么想法吗?
答案 0 :(得分:5)
您已使用filename = os.path.join(os.getcwd(), filename)
覆盖了变量,如果您将filename =
更改为filename
以外的其他内容,则您不会收到local variable 'filename' referenced before assignment
错误。
设置filename =
后,您不再引用传入的参数filename
,而是指的是您尝试使用的内部函数范围内的本地filename
在之前中使用它来定义它。
如果您将两行和其他变量更改为以下内容,那么您将遇到与dest相同的问题:
filename_ = os.path.join(os.getcwd(), filename)
dest_ = os.path.join(dest, filename)
您将看到代码运行正常,因为文件名现在引用的参数不是内部函数中定义的局部变量。
如果您尝试在第一个函数中重新分配x
并尝试在定义之前访问x
,则会看到完全相同的行为:
def makeInc(x, y):
def inc():
print y + x # will cause referenced before assignment error
x = 5 # now x is local to the inner func, the x from the outer function is overridden
return y + x
return inc
如果您打印__closure__
属性,您将看到会发生什么:
def makeInc(x, y):
def inc():
return y + x
return inc
inc5 = makeInc(5, 10)
inc10 = makeInc(10, 5)
print(inc5.__closure__)
(<cell at 0x7f180df67e50: int object at 0xef00f8>, <cell at 0x7f180df67fa0: int object at 0xef0080>)
现在重新分配x:
def makeInc(x, y):
def inc():
print y + x
x= 5
return y + x
return inc
inc5 = makeInc(5, 10)
inc10 = makeInc(10, 5)
print(inc5.__closure__)
(<cell at 0x7fea11889fd8: int object at 0x291e080>,)
重新分配内部函数后,不再引用x
。
所以基本上你们两个原始函数之间的根本区别在于,你在本地范围内重新分配变量,而在另一个中你不重新分配变量。从上面的代码可以看出,如果你在第一个函数中做了类似的事情,结果就完全一样了。
LEGB等范围内有一个很好的啧啧here。