0

I have a situation like the following:

functions = []
some_list = [('a', 'b'), ('c', 'd')]

for x, y in some_list:
    def foo(z):
        # do some things
        return "{} {} {}".format(x, y, z)
    functions.append(foo)

But obviously this doesn't work, as x and y will always have the last values they had in the loop, i.e. 'c' and 'd', i.e. both functions in the functions list will return '{} c d'.format(z) in practice.

How do I make it so that it does the string substitution immediately, so that on the first loop it defines foo(z) as the equivalent to

def foo(z):
    # do some things
    return "{} a b".format(z)

without any reference to the variables x and y?

EDIT: I now realize I also need to store a copy of foo in functions, not foo itself, but the rest of the question stll stands.

Kahr Kunne
  • 332
  • 1
  • 3
  • 14
  • Default parameter values are [famously](https://stackoverflow.com/q/1132941/3001761) bound at definition time. – jonrsharpe Nov 05 '17 at 16:57
  • Why do you need to have an array of functions? Wouldn't it be easier to accept the 3 parameters for `foo` and just pass them later? – arielnmz Nov 05 '17 at 17:00
  • @arielnmz the real example is a little bit more complicated and involves creating a class inside the loop using type(), with different variations of foo() then being added to its attributes. – Kahr Kunne Nov 05 '17 at 17:03
  • @jonrsharpe this does exactly what I want, but it feels like a bit of a hack – Kahr Kunne Nov 05 '17 at 17:03
  • You can use `functools.partial` to apply the function partially on each loop iteration, so you'd end up with a list of functions each bound to different `x` and `y` values. – bow Nov 05 '17 at 17:09

1 Answers1

1

You could bind the loop values to local variables at function definition time:

for x, y in some_list:
    def foo(z, a=x, b=y):
        return "{} {} {}".format(a, b, z)
    functions.append(foo)

functions[0]('foo')
'a b foo'

functions[1]('foo')
'c d foo'
user2390182
  • 72,016
  • 6
  • 67
  • 89