148

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Rodrigo
  • 5,938
  • 6
  • 31
  • 39

15 Answers15

172

Using all built-ins..

tuple(map(sum, zip(a, b)))
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
163
import operator
tuple(map(operator.add, a, b))
shahjapan
  • 13,637
  • 22
  • 74
  • 104
ironfroggy
  • 7,991
  • 7
  • 33
  • 44
  • 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
  • 18
    @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
42

This solution doesn't require an import:

tuple(map(lambda x, y: x + y, tuple1, tuple2))
Andy Hayden
  • 359,921
  • 101
  • 625
  • 535
Boaz Shvartzman
  • 421
  • 4
  • 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
22
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

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Mike
  • 1,107
  • 6
  • 7
  • 16
    This will work, but it's a bit heavy to import numpy just for a simple addition operation. –  Jul 17 '14 at 20:54
21

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
  • 92,761
  • 29
  • 141
  • 204
Dana
  • 32,083
  • 17
  • 62
  • 73
20

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
  • 364
  • 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
  • 899
  • 2
  • 12
  • 16
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
  • 7,161
  • 12
  • 41
  • 65
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,780
  • 2
  • 27
  • 37
3

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,865
  • 1
  • 27
  • 39
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,920
  • 19
  • 23
0

minimal class for all common numeric binary and unary operators
what i would want by default from a tuple-like data structure in any language

from math import ceil,floor,trunc
from operator import (add,and_,eq,floordiv,ge,gt,invert,le,lshift,lt,mod,mul,ne,
  neg,or_,pos,rshift,sub,truediv,xor,)
from itertools import repeat
from typing import Iterable
class ntuple(tuple):
  def __lt__(a,b): return ntuple(map(lt,a,a._b(b)))
  def __le__(a,b): return ntuple(map(le,a,a._b(b)))
  def __eq__(a,b): return ntuple(map(eq,a,a._b(b)))
  def __ne__(a,b): return ntuple(map(ne,a,a._b(b)))
  def __gt__(a,b): return ntuple(map(gt,a,a._b(b)))
  def __ge__(a,b): return ntuple(map(ge,a,a._b(b)))
  def __add__(a,b): return ntuple(map(add,a,a._b(b)))
  def __sub__(a,b): return ntuple(map(sub,a,a._b(b)))
  def __mul__(a,b): return ntuple(map(mul,a,a._b(b)))
  def __matmul__(a,b): return sum(map(mul,a,a._b(b)))
  def __truediv__(a,b): return ntuple(map(truediv,a,a._b(b)))
  def __floordiv__(a,b): return ntuple(map(floordiv,a,a._b(b)))
  def __mod__(a,b): return ntuple(map(mod,a,a._b(b)))
  def __divmod__(a,b): return ntuple(map(divmod,a,a._b(b)))
  def __pow__(a,b,m=None): return ntuple(pow(a,b,m) for a,b in zip(a,a._b(b)))
  def __lshift__(a,b): return ntuple(map(lshift,a,a._b(b)))
  def __rshift__(a,b): return ntuple(map(rshift,a,a._b(b)))
  def __and__(a,b): return ntuple(map(and_,a,a._b(b)))
  def __xor__(a,b): return ntuple(map(xor,a,a._b(b)))
  def __or__(a,b): return ntuple(map(or_,a,a._b(b)))
  def __neg__(a): return ntuple(map(neg,a))
  def __pos__(a): return ntuple(map(pos,a))
  def __abs__(a): return ntuple(map(abs,a))
  def __invert__(a): return ntuple(map(invert,a))
  def __round__(a,n=None): return ntuple(round(e,n) for e in a)
  def __trunc__(a): return ntuple(map(trunc,a))
  def __floor__(a): return ntuple(map(floor,a))
  def __ceil__(a): return ntuple(map(ceil,a))
  def _b(a,b): return b if isinstance(b,Iterable) else repeat(b,len(a))
ntuple((-1, 0, 2)) + (6, 4, 2) = (5, 4, 4)

ntuple((-1, 0, 2)) +  2 = (1, 2, 4)
ntuple((-1, 0, 2)) ** 2 = (1, 0, 4)
ntuple(( 1, 2, 3)) << 2 = (4, 8, 12)

-ntuple((-1, 0, 2)) = (1, 0, -2)

round(ntuple((-1.5, 0.6, 2.4))) = (-2, 1, 2)

sum(ntuple(a...)*(b...)) == ntuple(a...)@(b...)

Dan Oak
  • 704
  • 1
  • 7
  • 26
-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
  • 285
  • 1
  • 4
  • 13