8

Python not support adding a tuple to a list:

>>> [1,2,3] + (4,5,6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list

What are the disadvantages for providing such a support in the language? Note that I would expect this to be symmetric: [1, 2] + (3, 4) and (1, 2) + [3, 4] would both evaluate to a brand-new list [1, 2, 3, 4]. My rationale is that once someone applied operator + to a mix of tuples and lists, they are likely to do it again (very possibly in the same expression), so we might as well provide the list to avoid extra conversions.

Here's my motivation for this question.

It happens quite often that I have small collections that I prefer to store as tuples to avoid accidental modification and to help performance. I then need to combine such tuples with lists, and having to convert each of them to list makes for very ugly code.

Note that += or extend may work in simple cases. But in general, when I have an expression

columns = default_columns + columns_from_user + calculated_columns

I don't know which of these are tuples and which are lists. So I either have to convert everything to lists:

columns = list(default_columns) + list(columns_from_user) + list(calculated_columns)

Or use itertools:

columns = list(itertools.chain(default_columns, columns_from_user, calculated_columns))

Both of these solutions are uglier than a simple sum; and the chain may also be slower (since it must iterate through the inputs an element at a time).

max
  • 49,282
  • 56
  • 208
  • 355
  • 2
    How about simply being consistent? Use tuples or use lists, but not both of them. – Sven Marnach Mar 27 '12 at 20:30
  • If it was supported _at all_ I'd agree that would be the best behavior, but I think if someone does `2 + 'abc'` they clearly want `'2abc'` so why not allow it, etc. etc. -- it's too much interpretation. Explicit is better than implicit. – agf Mar 27 '12 at 20:30
  • 1
    You could simply use `itertools.chain` to chain your stuff and never use `+` instead. – hochl Mar 27 '12 at 20:32
  • I'd say that the weak point in your argument is the *"they are __likely__ to do it again"*. – Rik Poggi Mar 27 '12 at 20:39
  • You can't chain `extend`s like that. You'd have to `.extend(chain(...))`. – agf Mar 27 '12 at 20:42
  • @max: Good questions are single questions. You've asked two different "Why" and "How" questions here. The "Why" was first. – MattH Mar 27 '12 at 20:48
  • @max: My recommendation would be to forget trying to alter the built-in behaviour and write a simple function to concatenate sequences. E.g. (please excuse abuse of `or` used for brevity) `concatentate = lambda *x: reduce(lambda a,b: a.extend(b) or a,x,[])` – MattH Mar 27 '12 at 21:10

3 Answers3

12

This is not supported because the + operator is supposed to be symmetric. What return type would you expect? The Python Zen includes the rule

In the face of ambiguity, refuse the temptation to guess.

The following works, though:

a = [1, 2, 3]
a += (4, 5, 6)

There is no ambiguity what type to use here.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • I would expect the return to be a list, since once I start using + operator, it's likely I'll need to do it again. See my update to the question. – max Mar 27 '12 at 20:25
  • 3
    @max: I can't see why this should always return a list. I'm glad Python throws an error in such a situation, and I'm confident this won't ever change. – Sven Marnach Mar 27 '12 at 20:31
  • @max: You've asked *Why does Python not support adding a tuple to a list*. IMHO, Sven's answer nails this question. – MattH Mar 27 '12 at 20:33
  • @SvenMarnach Could you describe some circumstances under which you'd be happy that tuple + list raised an exception rather than silently work as I described? – max Mar 27 '12 at 20:41
  • @max, I could see it causing some very hard to track down errors. Say you have a method that only operates on tuples, but the + operator does an implicit and silent cast to list. In your code, you define a var to be a tuple, but then do a += with a list. Now you try to pass your var to your method and boom. And now you have a much harder to track down error than if you had had to deal with figuring out which type you wanted explicitly when you concatenated the collections. This goes doubly if your code is appending something that could be a tuple or a list to your var. – Silas Ray Mar 27 '12 at 21:19
  • @sr2222: I agree it would be a problem. But can you give me an example of a situation where you'd want to make a function that works on tuples but not lists? It seems to me that either such a function is terribly designed, and will cause endless annoyance to users regardless of how `+` behaves; or I am missing some obvious reasons why it might be useful. – max Mar 27 '12 at 21:24
  • @max: There are lots of functions that only work for tuples, but not for lists. Most notably, tuples may be hashable, while lists never are, so lists can never be used as dictionary keys. On a more general note, I think it is always almost a sign of a design error if my code tries to add lists and tuples, so I'd like to have this pointed out. I can still add an explicit conversion if this is indeed what I wanted. – Sven Marnach Mar 27 '12 at 23:00
  • Wow I forgot about hashing... And an interesting point about tuple + list being a symptom of something being wrong; I guess that's more to do with how you use these types in your code, but I can see how this approach is useful. – max Mar 28 '12 at 00:13
  • Last thing: even though I now understand the benefits, isn't the behavior of operator + an example of a major violation of duck typing, which I thought is one of the foundations of Pythonic programming style? I mean, when I pass a tuple to `operator +`, it has all the methods that a list has, so per duck typing, Python shouldn't complain... – max Mar 28 '12 at 00:16
  • @max: Duck typing doesn't mean to try by force to perform any operation with any type. Python is still a strongly typed language. Fortunately, it's not PHP. – Sven Marnach Mar 28 '12 at 00:21
  • "the `+` operator is supposed to be symmetric" - well, the operation of addition is symmetric in certain number systems (not all). Can you link an official document stating that the `+` operator in Python is supposed to be symmetric? Otherwise, this is incorrect. (In fact, I use it asymmetrically in my code for monetary conversions.) – kitti Jun 10 '15 at 17:52
  • @RyanP This is more of a philosophical statement than a strict technical one, as statements about language design tend to be. Even for built-in types, the `+` operator isn't always commutative (e.g. when used for string concatenation), but I still think that this answer accurately describes the rationale of the design decisions that led to not allowing to add lists and tuples. – Sven Marnach Jun 11 '15 at 10:37
  • 1
    This is CRAZY. List addition is NOT symmetric!!! A very simple/explicit semantic for sequence '+' is that is returns the type of its first arg. The function is ALREADY not symmetric. Seems a specious limitation to me. – Dan Oblinger Oct 03 '19 at 04:03
  • with abstract collections, this wouldn't hold in a statically typed language. A list and a tuple are both _some_ type of collection (probably `Sequence`), so you could return a `Sequence` in both cases. I guess that doesn't really work with dynamic typing though unless there is such a concrete type – joel Dec 04 '20 at 21:31
  • But this doesn't work when `a` is a tuple and the second line has a list eg. `a = (1, 2, 3); a += [4, 5, 6]` gives an error. Why is this? – MarcellPerger Mar 18 '23 at 19:06
  • @MarcellPerger That one is tricky. Because tuples are immutable, they don't implement `__iadd__()`, so `a += [4, 5, 6]` falls back to `a = a + [4, 5, 6]`. The right-hand side expression of this assignment is tuple + list again, which is not implemented due to ambiguity. Python's implementation of augmented assignment operators is confusing indeed. – Sven Marnach Mar 20 '23 at 09:29
2

Why python doesn't support adding different type: simple answer is that they are of different types, what if you try to add a iterable and expect a list out? I myself would like to return another iterable. Also consider ['a','b']+'cd' what should be the output? considering explicit is better than implicit all such implicit conversions are disallowed.

To overcome this limitation use extend method of list to add any iterable e.g.

l = [1,2,3]
l.extend((4,5,6))

If you have to add many list/tuples write a function

def adder(*iterables):
    l = []
    for i in iterables:
        l.extend(i)
    return l

print adder([1,2,3], (3,4,5), range(6,10))

output:

[1, 2, 3, 3, 4, 5, 6, 7, 8, 9]
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • 1
    He knows how to do it, he wants to know why he can't do it with `+`. – agf Mar 27 '12 at 20:27
  • @agf: *"and having to convert each of them to list makes for very ugly code"*. No, apparently he does not, because `.extend()` will not convert anything. – Rik Poggi Mar 27 '12 at 20:31
  • 1
    @RikPoggi He wants one long expression like `[1] + (2,) + (3,) + [4] + (5,)` to be able to be done at once without conversion or `chain` or anything like that without simplicity's sake. Any code that uses `extend` to do that in one line will be uglier than just that addition expression. – agf Mar 27 '12 at 20:34
  • @RikPoggi "My rationale is that once someone applied operator + to a mix of tuples and lists, they are likely to do it again (very possibly in the same expression), so we might as well provide the list to avoid extra conversions." See his new edit to the question he clearly knows about `extend`. – agf Mar 27 '12 at 20:40
  • Sorry, my fault for not writing more details in the original question. See the update. – max Mar 27 '12 at 20:42
  • @agf, I have added a `adder` to add iterables, which doesn't look ugly to me – Anurag Uniyal Mar 27 '12 at 20:48
  • @AnuragUniyal This `adder` function can be implemented via `list(itertools.chain(*iterables))`. – a_guest Oct 07 '19 at 11:08
1

You can use the += operator, if that helps:

>>> x = [1,2,3]
>>> x += (1,2,3)
>>> x
[1, 2, 3, 1, 2, 3]

You can also use the list constructor explicitly, but like you mentioned, readability might suffer:

>>> list((1,2,3)) + list((1,2,3))
[1, 2, 3, 1, 2, 3]
jterrace
  • 64,866
  • 22
  • 157
  • 202
  • It does if I only have one tuple to add. I often have several objects, some tuples, some lists. – max Mar 27 '12 at 20:23