5

I want to wrap a function with specified arguments, something like functools.partial, but it doesn't work as expected:

source_codes = (0, 1, 2)

def callback(source, *args):
    print 'callback from source: ', source

funcs = []
for source in source_codes:
    funcs.append(lambda *args: callback(source, *args))

for i, func in enumerate(funcs):
    print 'source expected: ', i
    func()
    print

the output:

source expected:  0
callback from source:  2

source expected:  1
callback from source:  2

source expected:  2
callback from source:  2

But...What I want is:

source expected:  0
callback from source:  0

source expected:  1
callback from source:  1

source expected:  2
callback from source:  2

I know it works if I use functools.partial, but I want to know the real problem in my code... Does the lambda wrapper use a global variable source ?

Community
  • 1
  • 1
cherish
  • 1,370
  • 1
  • 11
  • 16

2 Answers2

4

The issue in your code is that the lambda expression does not get evaluated untill it gets called.

And then when it gets called, it uses the latest value of source . It does not bind value of source at the time of creation of the lambda .

Example code to show this behavior -

>>> y = lambda: z
>>> z = 0
>>> y()
0
>>> z  = 1
>>> y()
1
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
2

partial already "freezes" the function's arguments and/or keywords you passed to him so you can simply eliminate lambda expression:

source_codes = (0, 1, 2)

def callback(source, *args):
    print 'callback from source: ', source

funcs = []
for source in source_codes:
    funcs.append(partial(callback, source))
    source = 30 # don't have any effects over partial function.

for i, func in enumerate(funcs):
    print 'source expected: ', i
    func()
    print
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119