I implemented two simple closures in Python. To me, they looks the same, but one works and the other doesn't.
The working one is:
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
But the second one doesn't work:
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
It faults at the first line of link()
when executing 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
They seem identical to me so I don't understand why the second one doesn't work. Any idea?
You have overwritten the variable with filename = os.path.join(os.getcwd(), filename)
, if you change the filename =
to something other than filename
you won't get a local variable 'filename' referenced before assignment
error.
Once you set filename =
you are no longer referring to the parameter filename
that is passed in you are referring to the local filename
in the scope of the inner function which you try to use in the if before you have it defined.
You will have the same problem with dest, if you change the two lines and the other variables to something like:
filename_ = os.path.join(os.getcwd(), filename)
dest_ = os.path.join(dest, filename)
You will see the code runs fine as filename now refers to the parameter not to a local variable defined in your inner function.
You will see the exact same behaviour if you try to reassign x
in your first function and try to access x
before you have defined it:
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
If you print the __closure__
attribute you will see what happens:
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>)
Now reassigning 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>,)
After reassigning in the inner function, there is no longer a reference to x
.
So basically the fundamental difference between your two original functions is that in one you are reassigning the variable in the local scope and in the other you are not. As you can see from the code above if you do something similar in the first function the outcome is exactly the same.
There is a nice tut here on scopes LEGB etc..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With