132

Is there anyway to get tuple operations in Python to work like this:

>>> a = (1,2,3)
>>> b = (3,2,1)
>>> a + b
(4,4,4)

instead of:

>>> a = (1,2,3)
>>> b = (3,2,1)
>>> a + b
(1,2,3,3,2,1)

I know it works like that because the __add__ and __mul__ methods are defined to work like that. So the only way would be to redefine them?

Rodrigo
  • 5,498
  • 6
  • 30
  • 39

14 Answers14

159
import operator
tuple(map(operator.add, a, b))
shahjapan
  • 12,609
  • 22
  • 69
  • 100
ironfroggy
  • 7,654
  • 6
  • 31
  • 43
  • 6
    I'd say this is the most pythonic solution. – Matthew Schinckel Jan 31 '09 at 01:34
  • 4
    Except that map() is semi-deprecated. See http://www.artima.com/weblogs/viewpost.jsp?thread=98196 for an article by Guido where it mentions how map is better written as a list comprehension. – Adam Parkin Feb 13 '12 at 21:07
  • It also blows up if a & b don't contain the same number of elements, or aren't "addable" (ex: `map(operator.add, (1,2), ("3", "4"))` – Adam Parkin Feb 13 '12 at 21:09
  • 28
    `tuple([item1 + item2 for item1, item2 in zip(a, b)])` would be the equivalent as a list comprehension. – Adam Parkin Feb 13 '12 at 21:20
  • @Adam Does the list comprehension also evaluate lazily? I'm all for a single way to do things so long as everything is evaluated lazily until I choose otherwise. – Eyal May 24 '12 at 06:47
  • @Eyal: I don't believe it's lazily evaluated, but I'm not entirely sure. – Adam Parkin May 25 '12 at 17:59
  • 17
    @AdamParkin, generator comprehensions are even better `tuple(item1 + item2 for item1, item2 in zip(a, b))`. – Cristian Ciupitu Nov 03 '16 at 08:10
  • You can also make it way shorter if you know the class `cls` of the objects `tuple(map(cls.__add__, a, b))`. For example if you know you have `ints`, `tuple(map(int.__add__, a, b))`. And maybe even generalize for any operation `tuple(map(cls.____, a, b))` – GT 77 Feb 11 '22 at 07:03
150

Using all built-ins..

tuple(map(sum, zip(a, b)))
Kenan Banks
  • 198,060
  • 33
  • 151
  • 170
38

This solution doesn't require an import:

tuple(map(lambda x, y: x + y, tuple1, tuple2))
Andy Hayden
  • 328,850
  • 93
  • 598
  • 514
Boaz Shvartzman
  • 381
  • 3
  • 2
  • 3
    This solution is also faster than the other no-import, one-liner solution (`map(sum, zip(a, b))`) – Air Dec 10 '13 at 00:14
21
from numpy import array

a = array( [1,2,3] )
b = array( [3,2,1] )

print a + b

gives array([4,4,4]).

See http://www.scipy.org/Tentative_NumPy_Tutorial

Community
  • 1
  • 1
Mike
  • 946
  • 5
  • 7
  • 14
    This will work, but it's a bit heavy to import numpy just for a simple addition operation. –  Jul 17 '14 at 20:54
20

Sort of combined the first two answers, with a tweak to ironfroggy's code so that it returns a tuple:

import operator

class stuple(tuple):
    def __add__(self, other):
        return self.__class__(map(operator.add, self, other))
        # obviously leaving out checking lengths

>>> a = stuple([1,2,3])
>>> b = stuple([3,2,1])
>>> a + b
(4, 4, 4)

Note: using self.__class__ instead of stuple to ease subclassing.

tzot
  • 87,612
  • 28
  • 135
  • 198
Dana
  • 30,443
  • 17
  • 60
  • 72
18

Generator comprehension could be used instead of map. Built-in map function is not obsolete but it's less readable for most people than list/generator/dict comprehension, so I'd recommend not to use map function in general.

tuple(p+q for p, q in zip(a, b))
Jaehyun Yeom
  • 344
  • 2
  • 5
6

All generator solution. Not sure on performance (itertools is fast, though)

import itertools
tuple(x+y for x, y in itertools.izip(a,b))
Mike
  • 829
  • 2
  • 12
  • 15
6

simple solution without class definition that returns tuple

import operator
tuple(map(operator.add,a,b))
DemonEye
  • 61
  • 1
  • 1
6

even simpler and without using map, you can do that

>>> tuple(sum(i) for i in zip((1, 2, 3), (3, 2, 1)))
(4, 4, 4)
LetsPlayYahtzee
  • 6,441
  • 10
  • 35
  • 61
3

Yes. But you can't redefine built-in types. You have to subclass them:

class MyTuple(tuple):
    def __add__(self, other):
         if len(self) != len(other):
             raise ValueError("tuple lengths don't match")
         return MyTuple(x + y for (x, y) in zip(self, other))
Doug
  • 8,440
  • 1
  • 26
  • 37
1

I currently subclass the "tuple" class to overload +,- and *. I find it makes the code beautiful and writing the code easier.

class tupleN(tuple):
    def __add__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x+y for x,y in zip(self,other))
    def __sub__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x-y for x,y in zip(self,other))
    def __mul__(self, other):
        if len(self) != len(other):
             return NotImplemented
        else:
             return tupleN(x*y for x,y in zip(self,other))


t1 = tupleN((1,3,3))
t2 = tupleN((1,3,4))
print(t1 + t2, t1 - t2, t1 * t2, t1 + t1 - t1 - t1)
(2, 6, 7) (0, 0, -1) (1, 9, 12) (0, 0, 0)
0

Here is another handy solution if you are already using numpy. It is compact and the addition operation can be replaced by any numpy expression.

import numpy as np
tuple(np.array(a) + b)
Hugues
  • 2,452
  • 21
  • 33
0

I keep coming back to this question, and I don't particularly like any of the answers as they are all answering the question for the general case, and I'm normally looking for the answer to a special case: I'm normally using a fixed tuple count, e.g. for n-dimensions.

   # eg adding a dx/dy to an xy point.
   # if I have a point xy and another point dxdy
   x, y = xy
   dx, dy = dxdy
   return x+dx, y+dy

while I normally shudder at unnecessary variables, the reason why I am unpacking a tuple is normally because I am working on the elements as individuals, and that is what is happening with tuple addition as requested above.

Konchog
  • 1,690
  • 16
  • 21
-2

In case someone need to average a list of tuples:

import operator 
from functools import reduce
tuple(reduce(lambda x, y: tuple(map(operator.add, x, y)),list_of_tuples))
ytutow
  • 275
  • 1
  • 4
  • 13