26

In a graphical program I'm writing using pygame I use a tuple representing a coordinate like this: (50, 50).

Sometimes, I call a function which returns another tuple such as (3, -5), which represents the change in coordinate.

What is the best way to add the change value to the coordinate value. It would be nice if I could do something like coordinate += change, but it appears that would simply concatenate the two tuples to something like (50, 50, 3, -5). Rather than adding the 1st value to the 1st value and the 2nd to the 2nd, and returning a resulting tuple.

Until now I've been using this rather tiresome method: coord = (coord[0] + change[0], coord[1] + change[1])

What is a better, more concise method to add together the values of two tuples of the same length. It seems especially important to know how to do it if the tuples are of an arbitrary length or a particularly long length that would make the previous method even more tiresome.

Keegan Jay
  • 960
  • 2
  • 10
  • 18

6 Answers6

34

Well, one way would be

coord = tuple(sum(x) for x in zip(coord, change))

If you are doing a lot of math, you may want to investigate using NumPy, which has much more powerful array support and better performance.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
John Y
  • 14,123
  • 2
  • 48
  • 72
17

List comprehension is probably more readable, but here's another way:

>>> a = (1,2)
>>> b = (3,4)
>>> tuple(map(sum,zip(a,b)))
(4,6)
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • 2
    I happen to find the comprehension/generator expression more readable, but I'm sure people whose minds work more "lispily" will find the map solution clearer. – John Y Jul 23 '09 at 05:40
  • I'm pretty lispy myself, but mostly prefer list comprehensions, since Haskell is also far from alien from my way of thinking. – Alex Martelli Jul 23 '09 at 06:11
  • Is the zip actually needed for this: >>> from operator import add >>> a = (1, 2) >>> b = (3, 4) >>> tuple(map(add, a, b)) (4, 6) >>> – Russel Winder Aug 02 '15 at 18:27
12

As John Y mentions, this is pretty easy using numpy.

import numpy as np

x1 = (0,3)
x2 = (4,2)
tuple(np.add(x1,x2))
Tom Roth
  • 1,954
  • 17
  • 25
3

This is a work in progress as I am learning Python myself. Can we use classes here, could simplify some operations later. I propose to use a coord class to store the coordinates. It would override add and sub so you could do addition and subtraction by simply using operators + and -. You could get the tuple representation with a function built into it.

Class

class coord(object):    
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __add__(self,c):
        return coord(self.x + c.x, self.y + c.y)

    def __sub__(self,c):
        return coord(self.x - c.x, self.y - c.y)

    def __eq__(self,c): #compares two coords
        return self.x == c.x and self.y == c.y

    def t(self): #return a tuple representation.
        return (self.x,self.y)

Usage

c1 = coord(4,3) #init coords
c2 = coord(3,4)

c3 = c1 + c2    #summing two coordinates. calls the overload __add__
print c3.t()    #prints (7, 7)
c3 = c3 - c1
print c3.t()    #prints (3, 4)
print c3 == c2  #prints True

you could improve coord to extend other operators as well (less than, greater than ..).

In this version after doing your calculations you can call the pygame methods expecting tuples by just saying coord.t(). There might be a better way than have a function to return the tuple form though.

Pradeep
  • 4,099
  • 4
  • 31
  • 45
3

To get your "+" and "+=" behaviour you can define your own class and implement the __add__() method. The following is an incomplete sample:

# T.py
class T(object):
    def __init__(self, *args):
        self._t = args
    def __add__(self, other):
        return T(*([sum(x) for x in zip(self._t, other._t)]))
    def __str__(self):
        return str(self._t)
    def __repr__(self):
        return repr(self._t)

>>> from T import T
>>> a = T(50, 50)
>>> b = T(3, -5)
>>> a
(50, 50)
>>> b
(3, -5)
>>> a+b
(53, 45)
>>> a+=b
>>> a
(53, 45)
>>> a = T(50, 50, 50)
>>> b = T(10, -10, 10)
>>> a+b
(60, 40, 60)
>>> a+b+b
(70, 30, 70)

EDIT: I've found a better way...

Define class T as a subclass of tuple and override the __new__ and __add__ methods. This provides the same interface as class tuple (but with different behaviour for __add__), so instances of class T can be passed to anything that expects a tuple.

class T(tuple):
    def __new__(cls, *args):
        return tuple.__new__(cls, args)
    def __add__(self, other):
        return T(*([sum(x) for x in zip(self, other)]))
    def __sub__(self, other):
        return self.__add__(-i for i in other)

>>> a = T(50, 50)
>>> b = T(3, -5)
>>> a
(50, 50)
>>> b
(3, -5)
>>> a+b
(53, 45)
>>> a+=b
>>> a
(53, 45)
>>> a = T(50, 50, 50)
>>> b = T(10, -10, 10)
>>> a+b
(60, 40, 60)
>>> a+b+b
(70, 30, 70)
>>> 
>>> c = a + b
>>> c[0]
60
>>> c[-1]
60
>>> for x in c:
...     print x
... 
60
40
60
mhawke
  • 84,695
  • 9
  • 117
  • 138
  • you would still need a function to convert from your type T to a tuple isn't. As you cannot use a+b in place of where u need a tuple. It will return an instance of type T. – Pradeep Jul 30 '09 at 12:21
  • @Sizzler: I think the "new" version addresses your concerns. – mhawke Jul 31 '09 at 00:50
  • It's cool, I would make it even better by using `zip_longest` (otherwise you lose information if you add short vector to long) and changing `repr` (since it's still different from tuple). Also, you don't need to define `__new__` at all, just define `__init__`! – ilya n. Aug 14 '09 at 00:00
  • @ilya n: `__new__` is overridden to provide the same object creation interface as `tuple`, otherwise you can't create an instance via `t = T(1,2,3)`. e.g try `class T(tuple): pass` and then `t = T(1,2,3)` will give you `TypeError: tuple() takes at most 1 argument (3 given)`. See http://docs.python.org/reference/datamodel.html#object.__new__ – mhawke Aug 14 '09 at 00:44
  • The examples are somewhat misleading. When you do `a+=b` you usually expects `a` to be the same object (unless the documentation explicitly states immutability). So you can easily implement `__iadd__/__isub__`. That being said, tuple is indeed immutable so I'm not sure what's more Pythonic. – Guy Sep 06 '16 at 05:51
1

My two cents, hope this helps

>>> coord = (50, 50)
>>> change = (3, -5)
>>> tuple(sum(item) for item in zip(coord, change))
(53, 45)
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
sunqiang
  • 6,422
  • 1
  • 32
  • 32
  • This is what I would use, but it would be clearer if you formatted this. Your answer is more general than Unknown's, but should be formatted like his/hers. – John Y Jul 23 '09 at 05:26
  • @John, sorry, my bad. I just insert the first line before click the button, maybe it broken the format. @Triptych, thanks! – sunqiang Jul 23 '09 at 05:32