0

I want to create a family of functions each with a different parameter. In trying to doing so I encountered an issue which I demonstrate below using a dummy code (in python):

Suppose I want 4 functions such that the i-th function prints i when called.

A Naive code to do this would be:

#------ Prepare the functions----
func_list =[]
for i in range(1,5):
    def dummy():
        print("Dummy no:", i)
    func_list.append(dummy)

#----- Call the functions -------    
for func in func_list:
    func()

But it results in the wrong output:

Dummy no: 4
Dummy no: 4
Dummy no: 4
Dummy no: 4

I figured the correct way is:

#------ Prepare the functions----
def return_a_func(k):
    def dummy():
        print("Dummy no:", k)
    return dummy

func_list =[]
for i in range(1,5):
    func_list.append(return_a_func(i))
    
#----- Call the functions -------  
for func in func_list:
    func()

This gives the correct output:

Dummy no: 1
Dummy no: 2
Dummy no: 3
Dummy no: 4

I vaguely understand that this has to do with closures. But I could not reason it out clearly and completely. Specifically why did the Naive approach fail? Why couldn't Python detect that 'i' must be bound to the function 'dummy'?

Edit: The following answer suggested in the comment is very useful: Creating functions in a loop

However, It would also be great to know the rationale behind having 'late binding' as a design choice. Also, when exactly does binding happen? For instance, this does not create a binding, although one would expect it to:

    i=1
    def dummy():
        p = i
        print("Dummy no:", p)
    f= dummy

    i=2
    f()
    # this print : Dummy no: 2
  • To answer the question in your title, the second technique, using a function factory, or closure, is the correct way. I'm not sure I have a good enough explanation but basically: during iteration, you are simply referencing `i`, whose value changes on every iteration. Every `dummy` function was a closure over a single reference to `i`. And when each function is called, it simply looks `i` for its value. With a proper closure, the argument's value is "captured" by the scope of the function factory. – ddejohn Mar 25 '22 at 04:19
  • When the factory-created function is called, it looks to its parent scope for the value. When the `for` loop-created function is called, it looks to its parent scope for the value, which is the global scope, in which the value of `i` was changed over the course of iteration. – ddejohn Mar 25 '22 at 04:22
  • I'm not super strong on Python internals, but this is how I understand it. – ddejohn Mar 25 '22 at 04:22
  • 1
    "It would also be great to know the rationale behind having 'late binding' as a design choice" That is a separate question, and also one that is probably off topic here; we don't generally discuss design decisions unless there is an authoritative reference for the explanation. There are subjective pros and cons to either (or any) approach. "For instance, this does not create a binding" Sure it does. If `i` is a mutable object, and the object is mutated in between via `i`, then `p` is affected (since it names the same object). Python's variables are names, which are a kind of binding. – Karl Knechtel Mar 25 '22 at 04:47
  • "late binding" doesn't mean that the binding process itself is "late"; it means that the thing that is bound *has its value looked up* late - i.e., *at the time when the value is needed*, and not sooner. If you evaluate `i` within the loop, it changes - because each time the value is needed for the evaluation, it has changed. But within the `dummy` functions, you get the same result - because *all the changes already happened* when the value is needed for `print`. – Karl Knechtel Mar 25 '22 at 04:50
  • The actual tricky thing is that the nested function `dummy` - in addition to being *created as a separate object* each time through the loop - will bind the *name* `i` to the outer `i`, rather than "substituting" it with the current *value*. In the simpler example, `p` is also bound to `i`, but they are in the same scope anyway. – Karl Knechtel Mar 25 '22 at 04:53
  • 1
    The reason the corrected approach works is that each *call to* `return_a_func` has its own stack frame, and therefore creates a separate `locals()` from which the inner `dummy` can look up its own `k` value. That said, a simpler and more direct way to solve the problem is to use `functools.partial`. – Karl Knechtel Mar 25 '22 at 04:56
  • `int` is immutable. Variable assignment is binding. There is no contradiction. In the non-working code, the `k` of the global code has *been rebound* before it is looked up. I think you are getting confused because you are too fixated on the concept of "binding", when really the question is about scope. – Karl Knechtel Mar 25 '22 at 04:57
  • Thank you for the explanation @KarlKnechtel. That makes it clear. So, p is assigned the value of i only when dummy is called and not before. And because dummy shares scope with i, which is changing, p changes as well. That makes sense. Thanks! – Chirag Shetty Mar 25 '22 at 05:02

0 Answers0