Search code examples
pythonlanguage-designlanguage-implementationpython-internals

Why must Python list addition be homogenous?


Can anyone familiar with Python's internals (CPython, or other implementations) explain why list addition is required to be homogenous:

In [1]: x = [1]

In [2]: x+"foo"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
C:\Users\Marcin\<ipython-input-2-94cd84126ddc> in <module>()
----> 1 x+"foo"

TypeError: can only concatenate list (not "str") to list

In [3]: x+="foo"

In [4]: x
Out[4]: [1, 'f', 'o', 'o']

Why shouldn't the x+"foo" above return the same value as the final value of x in the above transcript?

This question follows on from NPE's question here: Is the behaviour of Python's list += iterable documented anywhere?

Update: I know it is not required that heterogenous += work (but it does) and likewise, it is not required that heterogenous + be an error. This question is about why that latter choice was made.

It is too much to say that the results of adding a sequence to a list are uncertain. If that were a sufficient objection, it would make sense to prevent heterogenous +=. Update2: In particular, python always delegates operator calls to the lefthand operand, so no issue "what is the right thing to do" arises": the left-hand object always governs (unless it delegates to the right).

Update3: For anyone arguing that this is a design decision, please explain (a) why it is not documented; or (b) where it is documented.

Update4: "what should [1] + (2, ) return?" It should return a result value equal with the value of a variable x initially holding [1] immediately after x+=(2, ). This result is well-defined.


Solution

  • These bug reports suggest that this design quirk was a mistake.

    Issue12318:

    Yes, this is the expected behavior and yes, it is inconsistent.

    It's been that way for a long while and Guido said he wouldn't do it again (it's in his list of regrets). However, we're not going to break code by changing it (list.__iadd__ working like list.extend).

    Issue575536:

    The intent was that list.__iadd__ correspond exactly to list.extend(). There's no need to hypergeneralize list.__add__() too: it's a feature that people who don't want to get surprised by Martin-like examples can avoid them by using plain + for lists.

    (Of course, there are those of us who find this behaviour quite surprising, including the developer who opened that bug report).

    (Thanks to @Mouad for finding these).