On Wed, Dec 16, 2020 at 10:16:01PM +0300, Paul Sokolovsky wrote:
> With all the above in mind, Python3.7, in a strange twist of fate, and
> without much ado, has acquired a new operator: the method call, ".()".
No it hasn't. That's not a language feature, it is not documented as a
language feature, and it could be removed or changed in any release
without any deprecation or notice. It is a pure implementation detail,
not a language feature.
There is no documentation for a "method call" operator, and no
interpreter is required to implement either LOAD_ATTR or CALL_METHOD.
Byte code instructions are not part of the Python language. Every
interpreter is free to decide on whatever byte codes it likes, including
no byte codes at all. IronPython uses whatever primitives are offered by
the .Net CLR, Jython uses whatever the JVM offers. Nuitka intends to
generate C code; Brython generates Javascript.
Suppose that I forked CPython 3.7 or later, and made a *single change*
to it. When compiling expressions of the form
expr.name(...) # arbitrary number of arguments
my compiler looks for an environment variable PYOPTIMIZEMODE. If that
environment variable is missing, empty or false, the above expression
would be compiled using the old LOAD_ATTR and CALL_FUNCTION opcodes. But
if it existed and was true, the LOAD_METHOD and CALL_METHOD opcodes
would be used instead.
Two questions:
(1) Is this a legal Python implementation?
(2) Apart from some performance differences, what user-visible
difference to the behaviour of the code does that environment variable
cause?
I think that the answers are (1) Yes and (2) None whatsoever.
> It's a ternary operator with the following syntax:
>
> expr.name(args)
No it isn't. It is two pseudo-operators, one of which is a binary
"attribute lookup" operator:
expr.name
and the other is an N-ary "call" operator.
I say *pseudo* operator, because the meaning of "operator" is documented
in Python, and neither `.` not function call `( ... )` is included as
actual operators.
But the important thing here is that they are two distinct operations:
lookup the attribute, and call the attribute.
> Now, everything falls into its place:
>
> An expression like:
>
> expr.name
>
> is an "attribute access operator" which gets compiled to LOAD_ATTR
> instruction.
The language does not specify what, if any, instructions the dot will be
compiled to -- or even if it is compiled *at all*. A pure interpreter
with no compilation stage would still be a valid Python implementation
(although quite slow).
Because Python is Turing complete, we could implement a full Python
interpreter using a clockwork "Difference Engine" style machine, or
a Turing Machine, or by merely running the code in our brain. None of
these require the use of a LOAD_ATTR instruction.
The parens make **no semantic difference** which is what we have been
saying for **days**.
```
>>> dis.dis("(expr.name)()")
1 0 LOAD_NAME 0 (expr)
2 LOAD_METHOD 1 (name)
4 CALL_METHOD 0
6 RETURN_VALUE
>>> dis.dis("expr.name()")
1 0 LOAD_NAME 0 (expr)
2 LOAD_METHOD 1 (name)
4 CALL_METHOD 0
6 RETURN_VALUE
```
The CPython byte-code is identical, parens or no parens, but more
importantly, the *semantics* of the two expressions, as described by the
language docs, require the two to be identical.
And here is a bracketed expression when LOAD_ATTR gets used:
```
>>> dis.dis("(a:=expr.name)()")
1 0 LOAD_NAME 0 (expr)
2 LOAD_ATTR 1 (name)
4 DUP_TOP
6 STORE_NAME 2 (a)
8 CALL_FUNCTION 0
10 RETURN_VALUE
```
which categorically falsifies your prediction that a parenthesized
dot expression followed by call will use CALL_METHOD.
--
Steve
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/33JD23HHK7VQZGASX7DN6OKVJKIMGXYB/
Code of Conduct: http://python.org/psf/codeofconduct/