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/