Title: Language reference does not clearly describe modern operand coercion
Type: enhancement Stage: needs patch
Components: Versions: Python 3.9, Python 3.8
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: ncoghlan
Priority: normal Keywords:

Created on 2020-01-11 08:41 by ncoghlan, last changed 2020-01-11 08:41 by ncoghlan.

Messages (1)
msg359788 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2020-01-11 08:41
While reviewing ISO-IECJTC1-SC22-WG23's latest draft of their Python security annex, I found a description of operand coercion that was based on the legacy coercion model described at

That's still the second highest link if you search for "Python operand coercion", while the highest link is this old, very brief, summary from Python in a Nutshell: (still based on the old semantics where the inputs were coerced to a common type before calling the slot method, rather than giving the method direct access to the original operands).

The third highest link at least goes to PEP 208 (, which correctly describes the modern semantics, but it describes them in terms of the CPython C slot API, not the Python level special method APIs. does technically provide the required information, but it's implicit in the description of the numeric arithmetic methods, rather than being clearly spelled out as a clear description of "Python operand coercion". (There are also some oddities around operand coercion for three-argument pow() that I'm going to file as their own issue) references which describes defining new numeric ABCs in a coercion-friendly way, but still doesn't spell out the operand precedence and coercion dance.

We could likely improve this situation by adding a new "Special method invocation" subject at the end of, moving the existing "Special method lookup" subsection under it, and then adding a second subsection called "Operand precedence and coercion".

That new subsection would then cover the basic principles of:

* for unary operands, there is no ambiguity
* for binary operands of the same type, only the forward operation is tried (it is assumed that if the forward operation doesn't work, the reflected one won't either)
* for binary operands where the type of the RHS is a subclass of the type of the LHS, the reflected operation is tried first (if it exists), followed by the forward operation if the reflected call does not exist or returns the NotImplemented singleton
* for binary operands of unrelated types, the forward operation is tried first (if it exists), followed by the reflected operation if the forward call does not exist or returns the NotImplemented singleton
* for ternary operands (i.e. 3 argument pow()), the behaviour is currently implementation defined (as the test suite doesn't enforce any particular behaviour, and what CPython does isn't obviously useful)

Other specific points to be covered would be:

* any argument coercion that occurs is up to the individual method implementations
* logical short-circuiting expressions (and, or, if/else) only call the equivalent of bool(expr)

While the corresponding reflected operations for the binary operators are covered in the documentation of the forward operations, it would also likely be worthwhile including a summary table in this new subsection of exactly which special methods support reflection, and what the reflected method names are.
Date User Action Args
2020-01-11 08:41:52ncoghlancreate