What does "late binding closures" mean? [duplicate]
Asked Answered
S

2

11

I have a back-ground of C++ and trying to learn some python.

Whilst i understand virtual functions for C++, I unfortunately do not understand what is meant by late binding of closures in python.

Link: https://gist.github.com/deemson/8efabf56d67623ead804 (no longer works)

Copy-pasted from a tutorial:

functions = []
for n in [1, 2, 3]:
    def func(x):
        return n*x
    functions.append(func)

# You would expect this to print [2, 4, 6]
print(
    'calling a list of bad closures and output is: {}'
    .format(str([function(2) for function in functions]))
)

What exactly is happening here? When the function is appended in to the list, what values does it have? Can someone please simplify this code for me to understand?

Supersedure answered 6/4, 2016 at 22:33 Comment(0)
S
11

Notice this, you can create functions on runtime, more or less like lambdas in c++. So basically you are iterating over a list, making n take values 1,2 and 3

for n in [1, 2, 3]:
    def func(x):
        return n*x

so, by each iteration you are building a function named func, with takes a value and multiplies it for n. By appending it to the functions list you will have this functions stored, so you can iterate over the list to call the functions.

[function(2) for function in functions]

By doing this you call each of the functions stored with the value 2, you would expect this to output [2, 4, 6] ([1*2, 2*2, 3*2]), but instead it returns [6, 6, 6], WHY?, thats because every function use n for its computation, so they are not really doing 1*x, 2*x and 3*x but actually n*x and since n is bonded in last time to 3 all functions are doing 3*2 which becomes 6.

Play around with the python console to check it properly.

Signac answered 6/4, 2016 at 22:45 Comment(0)
D
3

In the language of C++, a pointer to the function is what's being appended to the list. After the for loop, functions contains pointers to three different functions (func(x) = n * x, func(x) = n * x and func(x) = n * x). Note the dependency on n. As n changes, so will the behavior of these functions, and they are all equivalent.

In the second part of the code, the pointers are extracted from the list and each of the three functions is evaluated with an argument of 2.

Here's a further example to clarify. Imagine we do this:

>>> functions
[<function func at 0x0239AA70>, <function func at 0x0239AAB0>, <function func at 0x0239AB30>]
>>> g = functions[2]
>>> g
<function func at 0x0239AB30>
>>> g(10)
20
>>> g(100)
200

What we're seeing in that first lines is that functions contains pointers to three different functions. The next line extracts the third pointer from the list (which refers to func(x) = n * x) and assigns it to g. Effectively, we have defined a function g(x) = n * x with this call. We can now evaluate g with arguments.

Note that since all functions depend on n, you could change n, and the behavior would change.

>>> n = 100
>>> g(10)
1000
Denumerable answered 6/4, 2016 at 22:50 Comment(3)
But would this not yeild [2 4 6] instead of [6 6 6]Supersedure
Daniel's answer is correct - he caught something I didn't. They all refer to n, and since n has settled at a value of 2 at the end, all functions will behave in the exact same way.Denumerable
I've amended to mention the dependency on n, and am leaving the solution posted to clarify the connection to pointers in C++.Denumerable

© 2022 - 2024 — McMap. All rights reserved.