My biggest complaint about Python is that it somehow doesn’t get flak for having the same (if not worse) scoping as JS, which gets endless hate for its function-scoped variables. (So much so that block scoped variables are the new normal in JS, but not in Py!) Take for instance: >>> powers_of_x = [lambda x: x^i for i in range(10)] >>> [f(2) for f in powers_of_x] [512, 512, 512, 512, 512, 512, 512, 512, 512, 512] To m…
You can achieve the same thing in python by using two lambdas (which is actually what you're doing in JS with map): >>> powers_of_x = [(lambda i: (lambda x: x**i))(i) for i in range(10)] >>> [f(2) for f in powers_of_x] [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] Or you can use default parameter values to use a single lambda (though this means it can be overridden, it's not semantically equivalent to the js implementation…
WTFPython – Understanding Python through surprising snippets
121–130 of 199 posts
Re: WTFPython – Understanding Python through surprising snippets
#122Earlier quoted context omitted.
Interesting, I don’t use python day-to-day, so I had assumed that the delayed capturing of the iteration variable was a lambda thing. Looks like this is still broken: for i in range(10): def mul(x): return i^x powers_of_x.append(mul) That to me coming from a JS background is totally wild
It’s actually due to python late binding. The variables are looked up at call time, rather than function define time. One could argue that your example shouldn’t work anymore in python3 since variables in list comprehensions go out of scope when they finish now. I haven’t tried it myself. I fill it under, “things I never need to do” :-)
for(var i=0; i x^i);
}
will behave the same way because it does the same thing, so will e.g. for i := 0; i
in Go.One of the confounding factors is that many languages have block-scoped for loops especially when using iterators instead of low-level C-style loops; that's also the case in Javascript when using `let` bindings (and is in fact one of the major reason to use `let` instead of `var` if that's possible).
The underlying concern is still there[1], but this very common failure case is avoided: rather than update a single binding, each iteration creates a brand new binding for the closure to close over.
Alternatively, a common mitigation technique is to emulate that using e.g. immediately invoked function expressions.
In Python you can also use the "default parameter" trick to shadow the closed-over binding (though most of the time this is used for performance reasons) without the overhead (both syntactic and runtime) of lambdas in lambdas in lambdas:
for i in range(10):
powers_of_x.append(lambda x, i=i: x^i)
[0] excluding the special case of low-level languages with capture clauses, as well as languages like Java where closing over mutable bindings is specifically forbidden (a lambda or anonymous class can only close over `final` bindings)[1] and remains a regular issue in async code fighting over closed-over context
Re: WTFPython – Understanding Python through surprising snippets
#123It's a good repo, but remember a lot of those are "A Good Thing™". The first snippet is a very good example: a := "wtf_walrus" doesn't work while: (a := "wtf_walrus") works. It's a fantastic design decision. Python took a long time before getting this operator, because it's a language that favors being readable, easy to use, and above all, to learn. But in many other languages, the very same operator is often misused…
Re: WTFPython – Understanding Python through surprising snippets
#124Earlier quoted context omitted.
I understand why people are willing to defend it or make apologies for it, but there really are some lessons to be learned from the mistakes it made, and dismissing legitimate criticism just ignores the opportunity.
I agree, there is a long list of things I would change in Python. And believe me when I say people involved in the community listen to this very carefully. This is why we had Python 3 in the first place. Because text handling was such a cause of pain. This is why we have type hints in the first place. Because big projects using Python felt let down. And Python 3 and type hints are also a huge source of criticism (I w…
If only people cared for fellow human beings, or earth, as much as they care for esoteric Wulrus operations in this or that language.
Re: WTFPython – Understanding Python through surprising snippets
#125If only it had curly braces instead of just indention. God, that kills me. And used unicode instead of ascii as the default. And there wasn't python 2.7 vs 3. Someone help me stop this list.
Re: WTFPython – Understanding Python through surprising snippets
#126Earlier quoted context omitted.
You can achieve the same thing in python by using two lambdas (which is actually what you're doing in JS with map): >>> powers_of_x = [(lambda i: (lambda x: x**i))(i) for i in range(10)] >>> [f(2) for f in powers_of_x] [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] Or you can use default parameter values to use a single lambda (though this means it can be overridden, it's not semantically equivalent to the js implementation…
Amusingly, the "default parameter" version relies on a feature that also gives rise to another WTPython entry: https://github.com/satwikkansal/wtfpython/blob/master/README...
The lambda case here is more like a default argument of a constant integer (immutable) than a default that is a list, which can be altered directly by the function it's a default of.
Re: WTFPython – Understanding Python through surprising snippets
#127Earlier quoted context omitted.
Amusingly, the "default parameter" version relies on a feature that also gives rise to another WTPython entry: https://github.com/satwikkansal/wtfpython/blob/master/README...
That's a different issue around default arguments that are mutable, rather than default arguments generally. The lambda case here is more like a default argument of a constant integer (immutable) than a default that is a list, which can be altered directly by the function it's a default of.
Re: WTFPython – Understanding Python through surprising snippets
#128Re: WTFPython – Understanding Python through surprising snippets
#129Re: WTFPython – Understanding Python through surprising snippets
#130Earlier quoted context omitted.
It’s actually due to python late binding. The variables are looked up at call time, rather than function define time. One could argue that your example shouldn’t work anymore in python3 since variables in list comprehensions go out of scope when they finish now. I haven’t tried it myself. I fill it under, “things I never need to do” :-)
It's not "python late binding", closures behave this way in any language with mutable bindings[0]. The entire point of a closure is to close over its lexical context, if the lexical context is mutable and mutated before the invocation of the closure, the closure is going to reflect that change. for(var i=0; i x^i); } will behave the same way because it does the same thing, so will e.g. for i := 0; i in Go. One of the…
std::vector> powers_of_x;
for (int i = 0; i
works as one would intuitively expect (a different i is captured for each iteration). The issue is with those languages that conflate values with references.