我正在使用Python2.7。*,当list是元组的元素时尝试列表操作的测试。
但是我找到了一些我无法理解的东西:
>>> a = ([],)
>>> a[0] = a[0] + [1,2,3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a
([],)
>>> b = ([], )
>>> b[0] += [1,2,3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> b
([1, 2, 3],)
>>> c = ([], )
>>> c[0].extend([1,2,3])
>>>
>>> c
([1, 2, 3],)
我知道元组是不可变的,列表是可变的,我知道list +=
相当于List += Tuple vs List = List + Tuple的list.extend()
。
但现在我在海上,如何解释上面的代码。
答案 0 :(得分:7)
在此代码中,您将分配给第一个元素,这在元组中是不可能的。
>>> a = ([],)
>>> a[0] = a[0] + [1,2,3]
此处,在右侧,您要从a[0]+[1,2,3]
创建一个新列表,然后尝试将其分配回a[0]
,这是不合法的。
在此代码中,您将检索元组的第一个元素,然后对其进行操作。
>>> c = ([], )
>>> c[0].extend([1,2,3])
当然,这是一个小的语义差异,但是在第一次尝试重新分配元组中的元素时。但让我们看一下幕后发生的事情:
在这里,我们将一个列表分配到x
,并观察其id在不同操作中的变化情况。
>>> x=[]
>>> id(x)
139987361764560
>>> id([])
139987361677112
>>> id(x+[1])
139987361829736
>>> x.append(1)
>>> x
[1]
>>> id(x)
139987361764560
请注意[]
,x
和x+[1]
的ID都不同,但我们添加x
后1
的ID仍然是相同。这再次强化了上面的陈述,即声明a[0] = a[0] + [1,2,3]
正在创建一个新列表,然后尝试将其分配给元组。
但是我们的元组是什么,让我们再次进行一些id检查:
>>> id(y)
139987361830168
>>> id(y[0])
139987361677112
>>> id(y[1])
139987361723384
>>> y[1] = y[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> y[1].append(1)
>>> id(y[1])
139987361723384
>>> y
([], [1])
同样,我们创建了一个元组,可以看到元组y
及其每个元素都有不同的ID。但请注意,我们甚至无法将相同的列表分配回元组中的相同位置。但是,我们可以按预期在该列表上执行操作,并看到元组更改。
关于不变性:这里要注意的重要一点是元组不会改变,因为元组中对象的id不会改变。< / p>
答案 1 :(得分:0)
在Python中,
a += b
解析为
a = a.__iadd__(b)
请参见参考文献here。如果a = a + b
具有a = a.__add__(b)
方法,则不会将其解析为a
或__iadd__
。
调用这些方法以实现增强的算术分配(+ =,-=,* =,@ =,/ =,// =,%=,** =,<< =,>> =,&= ,^ =,| =)。这些方法应尝试就地执行操作(修改self)并返回结果(可以是,但不一定是self)。如果未定义特定方法,则扩展分配将退回到常规方法。例如,如果x是具有 iadd ()方法的类的实例,则x + = y等同于x = x。 iadd (y)。否则,将x。添加(y)和y。 radd (x)视为对x + y的求值。
让我们看看这个例子
class MyList(list):
def __add__(self, x):
print('add')
return super().__add__(x)
def __iadd__(self, x):
print('iadd')
return super().__iadd__(x)
a = (MyList(),)
a[0] += [1]
在ipython中运行
In [84]: class MyList(list):
...: def __add__(self, x):
...: print('add')
...: return super().__add__(x)
...: def __iadd__(self, x):
...: print('iadd')
...: return super().__iadd__(x)
...:
...: a = (MyList(),)
...: a[0] += [1]
iadd
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-84-d737d44c0c44> in <module>
8
9 a = (MyList(),)
---> 10 a[0] += [1]
TypeError: 'tuple' object does not support item assignment
In [85]: a
Out[85]: ([1],)
这里有两个有趣的事情
iadd
被打印,因此a[0] += [1]
被解释为a[0] = a[0].__iadd__([1])
而不是a[0] = a[0] + [1]
a
也已更改。这意味着__iadd__()
有副作用也请参见官方python文档中的本节。它准确地解释了这种情况/您的问题。
https://docs.python.org/3/faq/programming.html#faq-augmented-assignment-tuple-error