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 -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/33JD23HHK7VQZGASX7DN6OKVJKIMGXYB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to