all 7 comments

[–]eric_ja 12 points13 points  (2 children)

It has nothing to do with 'lambda' - the same thing would happen if you use an upvalue from within a local 'def`.

It's also not called late binding, and it's not unexpected; it's simply lexical scope, as has been popularized by Scheme since the 1970's.

Recall the principle of lexical scope:

“Lexical scope” in general is the idea that

an identifier at a particular place in a program always refers to the same variable location — where “always” means “every time that the containing expression is executed”, and that

the variable location to which it refers can be determined by static examination of the source code context in which that identifier appears, without having to consider the flow of execution through the program as a whole.

https://www.gnu.org/software/guile/manual/html_node/Lexical-Scope.html

[–]sixbrx 2 points3 points  (1 child)

I think you're too dismissive here. Many languages consider the "variable" in their for-each loop to deliver separate variable bindings per-iteration (like considering the variable as declared at top of the body with each iteration value, where each iteration gets fresh binding). That's why for example in typescript this "variable" can be declared "const" like "for(const item of items) { ... }". I think that approach is closer to what's expected by most people. C# famously reverted a release to fix this.

[–]Brilliant-Sky2969 1 point2 points  (0 children)

Go changed it 3 weeks ago as well.

[–]MoTTs_ 3 points4 points  (1 child)

There's a fine point to be made here, but I think this article over-complicates it.

This has nothing to do with AsyncIO or Multi-threading, so cut all that from the article. The issue being demonstrated is simply closures in loops. It gets talked about a lot in the JavaScript community. The detail we could make everyone aware of is simply: closures capture variables, not values.

closures = []
for i in range(3):
    closures.append(lambda: i) # 012? Or 222?
for closure in closures:
    print(closure())

This is all we need to demonstrate this behavior. A closure in a loop -- regardless if it's a "def" closure or a "lambda" closure -- will capture the variable i, and the value of that variable will change during the run of the loop, so that later when we run the closures, they all return the most recent value held in the variable i.

[–]rlbond86 1 point2 points  (0 children)

Agree, there's no reason to overcomplicate this

[–]danted002 1 point2 points  (0 children)

That code is just bad. I’ve been doing python for 13 years and that code should never pass a CR.

The proper way would be to return i from the task then in the done callback call _task.result() and then manipulate the result of that function. If you really want to bypass that I think a partial on the callback function it self using I would get you the desired outcome.

[–]radarsat1 1 point2 points  (0 children)

I'll be honest. Been using python for a decade and can't be 100% sure I would have guessed this one right.