2

Is there a reason why python can not perform an in-place add operation when unpacking tuples, and is there a simple way around this?

E.g.

>>> x, y = (5, 0)
>>> x, y += (1, 8)
SyntaxError: illegal expression for augmented assignment

The alternatives are ugly and not very obvious for code maintenance:

>>> x, y = (5, 0)
>>> x, y = map(sum, zip((x, y), (1, 8)))
isedwards
  • 2,429
  • 21
  • 29
  • 2
    What are the tuples representing? You could build a class to hold the related values or, if there's only two, use complex numbers instead (where `.real` would be `x` and `.imag` would be `y`). – jonrsharpe Jul 23 '15 at 13:09
  • 1
    the alternate is not ugly my friend, thats the pythonic way – Ja8zyjits Jul 23 '15 at 13:09
  • The reason is that tuples are [immutables objects](https://docs.python.org/2/faq/design.html#why-are-there-separate-tuple-and-list-data-types), that's the difference between the lists. – bufh Jul 23 '15 at 13:29

4 Answers4

5

If you will always have two parts, you could use complex literals to represent the pair of values (so x would be the real part, and y the imaginary part):

>>> x_y = 5 + 0j
>>> x_y += 1 + 8j
>>> x_y.real, x_y.imag
(6.0, 8.0)

Obviously this looks a little complex (!) when you're only doing one operation, but if you're using lots of paired values it will work well. However, it is considered a hack and makes your code less readable (people may wonder what's doing on with .real and .imag).


A better alternative is to build your own numeric type to hold the related values. For example:

>>> from collections import namedtuple
>>> class XY(namedtuple('XY', 'x y')):
    def __repr__(self):
        return 'XY({0.x!r}, {0.y!r})'.format(self)
    def __add__(self, other):
        return XY(self.x + other.x, self.y + other.y)


>>> xy = XY(5, 0)
>>> xy += XY(1, 8)
>>> xy
XY(6, 8)

This is more readable and more flexible for adaptation to larger groups of numbers, whereas complex numbers can only hold two values. With minor tweaks, XY.__add__ could also accept any iterable of length two, so that e.g. xy += (1, 8) would also work.


In terms of why your original attempt doesn't work, note that the augmented assignment documentation states that (emphasis mine):

An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
4

You could use operator.add with * which might make your intentions more obvious that you are adding each subelement:

from operator import add
x, y = map(add, *((x, y), (1, 8)))

It is also documented grammar-token-augmented_assignment_stmt:

augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)

augtarget ::= identifier | attributeref | subscription | slicing

augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="

An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.

There is also an old discussion on mail.python.org discussing the issue.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
3

Not sure it fits with how Python tuples work normally:

>>> (1,2)+(3,4)
(1, 2, 3, 4)

rather than (4,6).

For some syntactic convenience, you can also do something like:

class pair(object):
    def __init__(self, *a):
        self.a = a
    def __add__(self, t):
        return map(sum, zip(self.a, t))

x, y = (5, 0)
x, y = pair(x, y) + (8, 1)
print x,y
Vlad
  • 18,195
  • 4
  • 41
  • 71
  • 1
    Why the downvote? What I'm saying is that if you allowed pair-wise addition syntax with unpacking, `(1,2) + (3,4)` should be equal to `(4,6)` for consistency. – Vlad Jul 23 '15 at 13:12
2

Tuples are immutable. You can only create a new tuple with the desired values.

Simeon Visser
  • 118,920
  • 18
  • 185
  • 180