[Twisted-Python] lambdas and DeferredList
I don't quite understand the behavior of lambdas in the following code: from twisted.internet import reactor, defer def someDeferred(val): d = defer.Deferred() reactor.callLater(1, d.callback, val) return d def done(result): print "all done" reactor.stop() def twothing(i): print "twothing: %s" % i return i def onething(result, i, callableThing): print "onething: %s, result %s" % (i, result) callableThing() def dlistspawn(): dlist = [] values = [(1, 'one'), (2, 'two'), (3, 'three')] for i, v in values: deferred = someDeferred(i) deferred.addCallback(onething, i, lambda: twothing(v)) dlist.append(deferred) dl = defer.DeferredList(dlist) dl.addCallback(done) return dl if __name__ == "__main__": dlistspawn() reactor.run() Which produces: onething: 1, result 1 twothing: three onething: 2, result 2 twothing: three onething: 3, result 3 twothing: three all done Why does the call to twothing() (via a lambda and callableThing) always bind to 'three'? How do I change the code to get it to bind successively to 'one', 'two', and 'three'? Thanks, Gary
On 8/3/07, gary jefferson <garyjefferson123@gmail.com> wrote:
I don't quite understand the behavior of lambdas in the following code:
[snip]
def dlistspawn(): dlist = [] values = [(1, 'one'), (2, 'two'), (3, 'three')] for i, v in values: deferred = someDeferred(i) deferred.addCallback(onething, i, lambda: twothing(v)) dlist.append(deferred) dl = defer.DeferredList(dlist) dl.addCallback(done) return dl
if __name__ == "__main__": dlistspawn() reactor.run()
Which produces: onething: 1, result 1 twothing: three onething: 2, result 2 twothing: three onething: 3, result 3 twothing: three all done
Why does the call to twothing() (via a lambda and callableThing) always bind to 'three'? How do I change the code to get it to bind successively to 'one', 'two', and 'three'?
The variable v gets bound when the lambda is invoked, not when constructed. -- - Henrik
gary jefferson wrote:
I don't quite understand the behavior of lambdas in the following code:
[...]
values = [(1, 'one'), (2, 'two'), (3, 'three')] for i, v in values: deferred = someDeferred(i) deferred.addCallback(onething, i, lambda: twothing(v)) dlist.append(deferred) dl = defer.DeferredList(dlist) dl.addCallback(done)
[...]
Which produces: onething: 1, result 1 twothing: three onething: 2, result 2 twothing: three onething: 3, result 3 twothing: three all done
Why does the call to twothing() (via a lambda and callableThing) always bind to 'three'? How do I change the code to get it to bind successively to 'one', 'two', and 'three'?
You don't need Twisted to see this: >>> values = ['one', 'two', 'three'] >>> functions = [] >>> for value in values: ... functions.append(lambda: value) ... >>> for func in functions: print func() ... three three three The simplest fix is use "lambda foo=foo: ...", to bind the value of the variable as it was at the time the lambda statement is executed to a local variable in that function. i.e.: >>> values = ['one', 'two', 'three'] >>> functions = [] >>> for value in values: ... functions.append(lambda v=value: v) ... >>> for func in functions: print func() ... one two three Refer to the Python documentation about scoping for more details. -Andrew.
participants (3)
-
Andrew Bennetts
-
gary jefferson
-
Henrik Thostrup Jensen