Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tuple assignment in Python, Is this a bug in Python? [duplicate]

I was reading this interesting post https://asmeurer.github.io/blog/posts/tuples/

At the footnote author present this example

>>> t=1,2,[3,4]
>>> t
(1, 2, [3, 4])
>>> t[2]+=[5,6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Although Python has raised a exception but it did change the tuple

>>> t
(1, 2, [3, 4, 5, 6])

Not sure what is going on here, is this a bug?

The behavior is same in 2.7.10 and 3.5.1

like image 586
DevC Avatar asked Sep 24 '16 07:09

DevC


2 Answers

So the behavior of += is a bit weird. For immutable objects like integers it has to assign a new object to the same name:

a = 4
a += 3

For mutable types, e.g. lists, the object is altered in place, but also returns the same object to be assigned to the same name. The first step works with your tuple, but not the second.

That's why after the list is extended the exception is raised.

like image 181
Daniel Avatar answered Oct 22 '22 10:10

Daniel


This is because the += operator (which is __iadd__, or in-place add internally) actually do return something after the assignment happened. In a list this translates into an extend call (or something like it) and thus the new items already got in, before the reference to the list was returned for assignment to t[2], which then raise the exception. Now you check the value you can see that it got added. The following is the minimum code to demonstrate this:

>>> class AddIDemo(object):
...     def __init__(self, items):
...         self.items = list(items) 
...     def __iadd__(self, other):
...         print('extending other %r' % other) 
...         self.items.extend(other.items)
...         print('returning self to complete +=')
...         return self 
...     def __repr__(self):
...         return self.items.__repr__() 
... 
>>> demo = AddIDemo([1, 2])
>>> demo += AddIDemo([3, 4]) 
extending other [3, 4]
returning self to complete +=
>>> demo 
[1, 2, 3, 4]
>>> t = 1, 2, demo 
>>> t 
(1, 2, [1, 2, 3, 4])
>>> 
>>> t[2] += AddIDemo([5, 6]) 
extending other [5, 6]
returning self to complete +=
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t 
(1, 2, [1, 2, 3, 4, 5, 6])
>>> 

Note that I added some more print statements to show that the function being called and how the operation happened as it would do in a standard list manipulation via += or __iadd__.

like image 45
metatoaster Avatar answered Oct 22 '22 09:10

metatoaster