Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> still a relatively simple language

If only. I suspect very few Python programmers can even fully explain what `a + b` does.

If `a` and `b` are instances of classes, many would say it's equivalent to `a.__add__(b)` or `type(a).__add__(a, b)`, but in fact it's much more complex.



I once broke some Python by changing a = a + b to a += b.

If a and b are lists, the latter modifies the existing list (which may be referenced elsewhere) instead of creating a new one.

I think Python is the only language I've encountered that uses the + operator with mutable reference semantics like this. It seems like a poor design choice.


This is one of the absolute worst design mistakes in Python. The example you give (that `a = a + b` and `a += b` aren't equivalent) is bad enough:

    >>> a = b = [1, 2, 3]                >>> a = b = [1, 2, 3]
    >>> a = a + [4]                      >>> a += [4]
    >>> a, b                             >>> a, b
    ([1, 2, 3, 4], [1, 2, 3])            ([1, 2, 3, 4], [1, 2, 3, 4])
What's worse is that sometimes, they are equivalent:

    >>> a = b = (1, 2, 3)                >>> a = b = (1, 2, 3)
    >>> a = a + (4,)                     >>> a += (4,)
    >>> a, b                             >>> a, b
    ((1, 2, 3, 4), (1, 2, 3))            ((1, 2, 3, 4), (1, 2, 3))
And even worse, in order to support a version of `a += b` that sometimes modifies `a` (e.g. with lists), and sometimes doesn't (with tuples), the implementation of the `+=` operator is convoluted, which can lead to:

    >>> t = ([1, 2, 3], ['a'])
    >>> t[0] += [4]
    TypeError: 'tuple' object does not support item assignment
    >>> t
    ([1, 2, 3, 4], ['a'])
The operation raises a TypeError, despite having succeeded!


Care to elaborate? Where is the complexity hidden?


1. operators use special lookup for dunder methods--on class, not on instance:

    >>> a+1 # lookup on class 
    1+1
    2 
    >>> a.__add__(1) # instance method 
    0
2. There is __radd__ that is called if __add__ doesn't support given types (for different types).


the full story is in the language reference, but the short answer is: __radd__ (unless it's more complicated than I realize)


Yes, the next level of complexity is that `a + b` will sometimes fall back to `b.__radd__(a)` if `a.__add__(b)` returns `NotImplemented`. But also:

- There are situations where `__radd__` takes priority over `__add__`. The rules for determining that priority are complex (and IIRC subtly different from the rules that determine whether `a < b` prioritises `a.__lt__(b)` or `b.__gt__(a)`).

- The lookup of `__add__` etc uses a special form of attribute lookup that's neither equivalent to `a.__add__` nor `type(a).__add__`. This special lookup only searches `type(a)` whereas the first would find an `__add__` function on `a`, and the second on `type(type(a))`.

I've also heard of further complications caused by implementation details leaking into the language semantics - for example, see Armin Ronacher's blog post: https://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-t...


I doubt many 4gl language developers could explain what a + b does in their respective language. That rabbit hole goes all the way down to the physical instruction execution on silicon.


Meanwhile some 3GL language developers, think they can explain.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: