[Python编程(第4版)].(Programming.Python.4th.Edition).Mark.Lutz.文字版

(yzsuai) #1

In terms of GUIs, this becomes significant most often when you generate callback han-
dlers within loops and try to use enclosing scope references to remember extra data
created within the loops. If you’re going to make functions within a loop, you have to
apply the last example’s behavior to the loop variable:


def odd():
funcs = []
for c in 'abcdefg':
funcs.append((lambda: c)) # c will be looked up later
return funcs # does not remember current c

for func in odd():
print(func(), end=' ') # OOPS: print 7 g's, not a,b,c,...!

Here, the func list simulates registered GUI callback handlers associated with widgets.
This doesn’t work the way most people expect it to. The variable c within the nested
function will always be g here, the value that the variable was set to on the final iteration
of the loop in the enclosing scope. The net effect is that all seven generated lambda
functions wind up with the same extra state information when they are later called.


Analogous GUI code that adds information to lambda callback handlers will have sim-
ilar problems—all buttons created in a loop, for instance, may wind up doing the same
thing when clicked! To make this work, we still have to pass values into the nested
function with defaults in order to save the current value of the loop variable (not its
future value):


def odd():
funcs = []
for c in 'abcdefg':
funcs.append((lambda c=c: c)) # force to remember c now
return funcs # defaults eval now

for func in odd():
print(func(), end=' ') # OK: now prints a,b,c,...

This works now only because the default, unlike an external scope reference, is evalu-
ated at function creation time, not at function call time. It remembers the value that a
name in the enclosing scope had when the function was made, not the last assignment
made to that name anywhere in the enclosing scope. The same is true even if the func-
tion’s enclosing scope is a module, not another function; if we don’t use the default
argument in the following code, the loop variable will resolve to the same value in all
seven functions:


funcs = [] # enclosing scope is module
for c in 'abcdefg': # force to remember c now
funcs.append((lambda c=c: c)) # else prints 7 g's again

for func in funcs:
print(func(), end=' ') # OK: prints a,b,c,...

The moral of this story is that enclosing scope name references are a replacement for
passing values in with defaults, but only as long as the name in the enclosing scope will


390 | Chapter 7: Graphical User Interfaces

Free download pdf