6

Why is this not working ?

u = {}
for me in ['foo', 'bar']:
   def callback():
       return 'I am %s' % me
   u[me] = callback

The output I get is:

>>> u['foo']()
'I am bar'

It seems the callback is defined once at the latest iteration.

EDIT

As proposed by kawadhiya21, a class approach would work:

class CallbackFactory():
    def __init__(self, me):
       self.me = me

    def __call__(self):
       return 'I am %s' % self.me

u = {}
for me in ['foo', 'bar']:

   u[me] = CallbackFactory(me)

But it is far more complicated than the former approach.

nowox
  • 25,978
  • 39
  • 143
  • 293

3 Answers3

11

All your functions are referencing the same variable me. When you try and call the function, me has the value 'bar', because that's what it was when the loop ended.

If you want me to take a different value for each definition of the function, then each function needs its own copy of the variable.

u = {}
for me in ['foo', 'bar']:
   def callback(me_copy=me):
       return 'I am %s' % me_copy
   u[me] = callback

If you wanted, you could shorten that to:

u = {}
for me in ['foo', 'bar']:
   u[me] = lambda me_copy=me: 'I am %s'%me_copy

or even

u = { me: (lambda me_copy=me: 'I am %s'%me_copy) for me in ('foo', 'bar') }
khelwood
  • 55,782
  • 14
  • 81
  • 108
2

First off, in your example, u['foo'] should not return a string at all, but a function handle. :) EDIT: this sentence is no longer relevant.

Second, your function callback uses me, from the outer scope. If you change this value (in your case by your loop) in the outer scope, then the function's output also changes.

To fix this, you need to make me part of the function's own scope, e.g. by doing:

def callback(int_me=me):
    return 'I am %s' % int_me
acdr
  • 4,538
  • 2
  • 19
  • 45
1

You want to define a family of callbacks, not simply save a bunch of outputs, right? Define a function that binds the parameter you want, like this:

def make_callback(param):
    def callback():
       return 'I am %s' % param
    return callback

for me in ['foo', 'bar']:
    u[me] = make_callback(me)

>>> u["foo"]()
'I am foo'
alexis
  • 48,685
  • 16
  • 101
  • 161