Late Binding Closures
Another common source of confusion is the way Python binds its variables in closures (or in the surrounding global scope).python
What You Wrote
def create_multipliers():
return [lambda x : i * x for i in range(5)] What You Might Have Expected to Happen
for multiplier in create_multipliers(): print multiplier(2) A list containing five functions that each have their own closed-over i variable that multiplies their argument, producing:express
0
2
4
6
8
What Does Happen
8
8
8
8
8
Five functions are created, but all of them just multiply x by 4.app
Python’s closures are late binding. This means that names within closures are looked up at the time the inner function is called.ide
Here, whenever any of the returned functions are called, the value of i is looked up in the surrounding scope at call time, when by then the loop has completed and i is left with its final value of 4.oop
What’s particularly nasty about this gotcha is the seemingly prevalent misinformation that this has something to do with lambdas in Python. Functions created with a lambda expression are in no way special, and in fact the same exact behavior is exhibited by just using an ordinary def:ui
def create_adders(): for i in range(5): def adder(x): return i * x yield adder
What You Should Do Instead
Well. Here the general solution is arguably a bit of a hack. Due to Python’s afformentioned behavior concerning evaluating default arguments to functions (see Mutable Default Arguments), you can create a closure that binds immediately to its arguments by using a default arg like so:this
def create_adders(): return [lambda x, i=i : i * x for i in range(5)]
When the Gotcha Isn’t a Gotcha
When you want your closures to behave this way. Late binding is good in lots of situations. Looping to create unique functions is unfortunately a case where they can cause hiccups.
轉自lua