3 个回答

-1

下面这段话:

>>> a[0:1] = [1]

是把列表 [1] 作为列表 a 的一个子集,从 01 进行赋值。

当你执行 a[0:1][0] 时,你实际上是在获取列表 [3] 的第一个元素,也就是 3。如果你尝试把它的值改成 1,那是行不通的,因为 3 不能变成 1。不过,如果你只使用 a[0:1],你得到的是 [3],而这个 是可以 改成 [1] 的。希望这能帮到你。

示例

>>> a = [1,2,3,4]
>>> a[1:4]
[2,3,4]

>>> a[1:4] = [6,5,4,3,2]
>>> a
[1,6,5,4,3,2]
9

切片一个列表会创建一个浅拷贝,这并不是对原始列表的引用。所以当你得到这个切片时,它并不和原来的列表a绑定在一起。因此,你可以尝试去改变其中的一个元素,但因为这个切片没有存储在一个变量里,所以对原始列表list不会有任何改变。

为了更清楚地说明,前面的操作是通过__getitem__来访问列表的一部分(一个拷贝):

a[0:1][0] = 1

你正在编辑切片[0:1],这只是a的一个浅拷贝,所以不会改变a本身。

而后面的操作是调用__setitem__,这当然会直接在原地修改对象:

a[0:1] = [1]

你是直接引用并编辑a的一部分,所以它会实时改变。

9

内部来说,这两者有很大的区别:

>>> a = [3, 2]
>>> a[0:1][0] = 1

其实是一个简写形式,等同于

temp = a[0:1]
temp[0] = 1

而且在内部表示为

a.__getitem__(slice(0, 1)).__setitem__(0, 1)

分别是。

temp = a.__getitem__(slice(0, 1))
temp.__setitem__(0, 1)

所以,它会访问列表中的一部分,创建一个单独的对象,并在这个对象上进行赋值操作,然后这个对象就被丢弃了。

另一方面,

>>> a[0:1] = [1]

则是

a.__setitem__(slice(0, 1), [1])

它只是对原始对象进行操作。

所以,虽然看起来相似,这些表达式在意义上是不同的。

让我们来测试一下:

class Itemtest(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return self.name
    def __setitem__(self, item, value):
        print "__setitem__", self, item, value
    def __getitem__(self, item):
        print "__getitem__", self, item
        return Itemtest("inner")

a = Itemtest("outer")
a[0:1] = [4]
temp = a[0:1]
temp[0] = 4
a[0:1][0] = 4

输出结果是

__setitem__ outer slice(0, 1, None) [4]
__getitem__ outer slice(0, 1, None)
__setitem__ inner 0 4
__getitem__ outer slice(0, 1, None)
__setitem__ inner 0 4

撰写回答