Python:list是元组中的元素时的列表操作

时间:2014-01-15 04:56:49

标签: list python-2.7

我正在使用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 + Tuplelist.extend()
但现在我在海上,如何解释上面的代码。

2 个答案:

答案 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

请注意[]xx+[1]的ID都不同,但我们添加x1的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],)

这里有两个有趣的事情

  1. iadd被打印,因此a[0] += [1]被解释为a[0] = a[0].__iadd__([1])而不是a[0] = a[0] + [1]
  2. 即使分配失败,a也已更改。这意味着__iadd__()有副作用

也请参见官方python文档中的本节。它准确地解释了这种情况/您的问题。

https://docs.python.org/3/faq/programming.html#faq-augmented-assignment-tuple-error