On Wed, Jan 13, 2021 at 8:02 PM Ethan Furman
On 1/12/21 11:27 PM, Chris Angelico wrote:
On Wed, Jan 13, 2021 at 6:11 PM Ethan Furman wrote:
Optimizations are an implementation detail, and implementation details should not change the language.
The language can also be defined in an optimization-friendly way, though. Consider how we got positional-only arguments in Python: first they existed in C-implemented functions in CPython, even though they couldn't exist in pure Python code, and then the functionality got added to the language definition, thus permitting the optimization.
1. What optimization? 2. Did the language change because of the optimization?
It's a lot faster for C-implemented functions to require positional parameters. A number of functions had help text that implied that they accepted keyword-or-positional args, but if you tried to call them with keyword args, they'd error out. And yes. PEP 457 and then PEP 570 introduced real positional-only arguments, and part of the debate surrounded the question "should we require that all C-implemented functions support keyword args?". Why have a performance hit on all C functions just for the sake of an arbitrary consistency? Instead, the language definition was changed to make this possible - and now we have, for instance, "len(obj, /)", which clearly does not accept len(obj="x"), but does accept len("x").
Or consider dictionary lookup. Most people treat it as "find a key which is equal to the one you're looking for", but the actual definition is "find a key which is identical to, or equal to, the one you're looking for".
Exactly. The definition, i.e. language spec, says identity, then equality.
Right. The definition is set to permit the optimization. Therefore the optimization cannot be in violation of the spec.
The topic under discussion is a language definition. Choosing to permit the optimization doesn't mean that the implementation detail changes the language. Choosing to deny it means there won't be an optimization.
There are, I am sure, many optimizations that are not possible because of Python's dynamism. `if <something>` is supposed to evaluate `bool(something)`, regardless of what comes after.
It doesn't, though. There is a difference between: if a and b: ... and x = a and b if x: ... The first one will boolify a only once; the second will figure out what bool(a) is before the assignment, and then (if it's false) boolify it again in the 'if' statement. Current builds of Python 3.10 optimize this away too: if a or 1: print(1) Regardless of the truthiness of a, the print call will happen. (Note that a is still *evaluated*. It's just not queried for truthiness. For instance, "if f() or 1:" will still call the function; it just won't call __bool__() on the return value.)
I personally don't see any reason to force Python to calculate something unnecessarily, given that this is *already* happening in other situations (see the "if a and b:" optimization, which doesn't boolify twice).
Sure, and I agree that calling `bool()` a second time is wasteful, as well as possibly confusing -- Python already has the answer from the first `bool()` call, so why would it need to do it again? That seems a matter of correctness, not optimization.
Is it? It means that there's a visible difference between putting it in a variable and doing it inline. If it's okay to do that, then why is it not okay to remove the bool check when it can't affect the result? In theory, "x = (expr)" followed by "if x:" should perform the exact same checks as "if (expr):"; if it's okay to violate that principle for "if a and b:", then why not for "if a: pass"? Either way, the interpreter knows that any sane __bool__ function cannot affect the result. ChrisA