Python中的切片 - 是拷贝还是指针?
>>> a = [3, 2]
>>> a[0:1][0] = 1
>>> a
[3, 2]
>>> a[0:1] = [1]
>>> a
[1, 2]
a[0:1]
是什么意思呢?
我觉得这两种结果是相互矛盾的。你能帮我理清这个问题吗?
3 个回答
-1
下面这段话:
>>> a[0:1] = [1]
是把列表 [1]
作为列表 a
的一个子集,从 0
到 1
进行赋值。
当你执行 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