Hence the former can be used in contexts like "if x := 10: pass", which is the whole point of the PEP.
Hence the former can be used in contexts like "if x := 10: pass", which is the whole point of the PEP.
You could, but that would turn "syntactic sugar" into a useless phrase with arbitrary meaning.
The phrase "syntactic sugar" is usually reserved for language constructs which can always be rewritten, in-place, to some other construct in the same language, such that the semantics is identical (i.e. we can't tell which construct was used, unless we parse the contents of the file).
Python has examples like `foo += bar` being sugar for `foo = foo + bar`.
As an aside, your mention of "machine language" implies the use of operational semantics. That's where we say the "meaning" of a program depends on what it does to the machine when executed. That's fine, but it's not the only approach to semantics. In particular denotational semantics defines the meaning of a program by giving a meaning to each syntactic element of the language and their combinations, usually by rewriting them into some other, well-defined language (e.g. set theory). I much prefer denotational semantics, since it lets me 'think in the language', rather than making me 'think like the machine'.
Nitpick, but I don't think that's true – AFAIK they translate into different method calls.
`foo + bar` →`foo.__add__(bar)`
`foo += bar` → `foo.__iadd__(bar)`
(note the `i` in the second one) >>> class A(object):
... def __getattribute__(self, attr):
... if attr == "__add__":
... return lambda *_: "hello world"
... return None
...
>>> a = A()
>>> a.__add__(A())
'hello world'
>>> a + A()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'A' and 'A'
if '__add__' in a.__dict__:
try:
return a.__add__(b)
except NotImplemented:
if '__radd__' in b.__dict__:
return b.__radd__(a)
elif '__radd__' in b.__dict__:
return b.__radd__(a)
raise TypeError('unsupported operand type(s) for +: '{}' and '{}'.format(type(a), type(b))
In particular, the runtime seems to directly look for '__add__' in the object's __dict__, rather than just trying to invoke `__add__`, so your `__getattribute__` method isn't enough to make it work. If you add an actual `__add__` method to A your example will work.I'm reminded of PHP, where (at least in version 5.*) we could write:
$myObject->foo = function() { return "hello world"; };
$x = $myObject->foo;
$x(); // Hello world
$myObject->foo(); // Error: no such method 'foo'
(Taken from an old comment https://news.ycombinator.com/item?id=8119419 )https://docs.python.org/3/reference/datamodel.html#special-l...
Apparently the runtime is even more picky than I showed. The method has to be defined on the object's type, not in the object's instance dictionary. So, really the lookup is something like:
if hasattr(type(a), '__add__'):
The link I provided explains the rationale for bypassing the instance dictionary and `__getattribute__` method.