A nested function can access (ro by default) the variables of the enclosing scope. To modify them, the keyword nonlocal is required.
The error below is due to 'late binding' and makes me wonder why one would use this. Supposedly, it prevents the need for a global variable. Personally, I would rather use a class. An exception is if one is writing decorators.
This is not caused by the lambda. A nested (named) function inside of mul() would have the same problem.
In [73]:
def mul_broke(count):
return [lambda x: x*i for i in range(count)]
print 'Expected [0, 2, 4, 6]'
print 'Actual ', [m(2) for m in mul_broke(4)]
In [70]:
def mul(count):
'''
Due to Python’s aforementioned behavior concerning evaluating
default arguments to functions, you can create a closure that
binds immediately to its arguments by using a default arg.
'''
return [lambda x, i=i: x*i for i in range(count)]
print 'Expected [0, 2, 4, 6]'
print 'Actual ', [m(2) for m in mul(4)]
More about closures
In [ ]:
def print_msg(msg):
def printer():
print(msg) # Has access to outer msg
return printer # Interesting!
In [54]:
print_msg('First')()
Lets bind a variable another to the returned function printer(). Strangely, it remembered the value of msg. This is a closure.
In [55]:
another = print_msg('Second')
another()
It does not matter if msg goes out of scope or the function is deleted.
In [56]:
del print_msg
another()
As seen from the above example, we have a closure in Python when a nested function references a value in its enclosing scope. The criteria that must be met to create closure in Python are summarized in the following points.
In [19]:
class P(object):
x = 1
class A(P):
pass
class B(P):
pass
print 'P A B\n-----'
print P.x, A.x, B.x
A.x = 2
print P.x, A.x, B.x, '<-- A gets its own x via assignment'
P.x = 3
print P.x, A.x, B.x, '<-- B is still sharing Ps x'
In [47]:
def foo(x, my_list=None):
if my_list is None:
my_list = []
my_list.append(x)
return my_list
print 'foo (correct)\n-------------'
print foo('s'), foo('t'), foo('u')
def bar(x, my_list=[]):
my_list.append(x)
return my_list
print '\nbar (broken)\n------------'
print bar('s'), bar('t'), bar('u')
In [33]:
import pprint
L = [[]] * 5 # Creates a list of 5 references to the same [].
print L
L[0].append(10) # Append to the first, but they are all the same thing.
print L
L[1].append(20) # Append to the second, but still the same.
print L
L.append(30) # Append to the outer list.
print L
In [49]:
L = [1,2,4,5,6,8,-2]
# 0,1,2,3,4,5,6
# Soln 1 with enumerate
L_enum = [i for n,i in enumerate(L) if (i%2 == 0 and n%2 == 0)]
# Soln 2 with slicing
L_sliced = [i for i in L[::2] if i%2 == 0]
print L, '-->', L_enum
print L, '-->', L_sliced
In [42]:
a = 255
b = 255
print a is b
c = 512
d = 512
print c is d
In [44]:
i = [1,2,3]
j = list(i)
k = i
i.append(4)
print 'i', i
print 'j', j
print 'k', k
In [46]:
import re
import unittest
def space20(strg):
'''Replace " " with "%20"'''
space_to_20 = re.compile(r' ')
return space_to_20.sub(r'%20', strg)
class Space20Test(unittest.TestCase):
def test_zero_len(self):
self.failUnlessEqual('', space20(''))
def test_match_nothing(self):
self.failUnlessEqual('abc;def', space20('abc;def'))
def test_match_at_start(self):
self.failUnlessEqual('%20abc', space20(' abc'))
def test_match_in_middle(self):
self.failUnlessEqual('abc%20def', space20('abc def'))
def test_match_at_end(self):
self.failUnlessEqual('abc%20', space20('abc '))
def test_match_complex(self):
self.failUnlessEqual('%20%20abc%20%20def%20%20',
space20(' abc def '))
squares_suite = unittest.TestLoader().loadTestsFromTestCase(Space20Test)
unittest.TextTestRunner().run(squares_suite)
Out[46]: