简短的回答
切片列表不会生成列表中对象的副本;它只是将引用复制到这些对象。这就是问题的答案。
长话短说
可变和不可变值的测试
首先,让我们测试一下基本的声明。我们可以证明,即使在整数这样的不可变对象的情况下,也只复制引用。下面是三个不同的整数对象,每个对象都具有相同的值:>>> a = [1000 + 1, 1000 + 1, 1000 + 1]
它们具有相同的值,但您可以看到它们是三个不同的对象,因为它们具有不同的ids:>>> map(id, a)
[140502922988976, 140502922988952, 140502922988928]
切片它们时,引用保持不变。未创建新对象:>>> b = a[1:3]
>>> map(id, b)
[140502922988952, 140502922988928]
使用具有相同值的不同对象表明复制过程不需要interning——它只是直接复制引用。
使用可变值进行测试会得到相同的结果:>>> a = [{0: 'zero', 1: 'one'}, ['foo', 'bar']]
>>> map(id, a)
[4380777000, 4380712040]
>>> map(id, a[1:]
... )
[4380712040]
检查剩余内存开销
当然,引用本身是复制的。在64位计算机上,每一个字节需要8个字节。每个列表都有72字节的内存开销:>>> for i in range(len(a)):
... x = a[:i]
... print('len: {}'.format(len(x)))
... print('size: {}'.format(sys.getsizeof(x)))
...
len: 0
size: 72
len: 1
size: 80
len: 2
size: 88
正如Joe Pinsonaultreminds us,这一开销加起来。整数对象本身并不是很大——它们比引用大三倍。所以这在绝对意义上为您节省了一些内存,但是渐进地说,能够将多个“视图”列表放在同一内存中可能是很好的。
使用视图节省内存
不幸的是,Python提供了一种不容易的方法来生成列表中的“视图”对象。或者我应该说“幸运”!这意味着您不必担心切片来自何处;对原始切片的更改不会影响切片。总的来说,这使得对程序行为的推理更加容易。
如果您真的想通过使用视图来节省内存,请考虑使用numpy数组。当切片一个numpy数组时,内存在切片和原始数组之间共享:>>> a = numpy.arange(3)
>>> a
array([0, 1, 2])
>>> b = a[1:3]
>>> b
array([1, 2])
当我们修改a并再次查看b时会发生什么?>>> a[2] = 1001
>>> b
array([ 1, 1001])
但这意味着您必须确保在修改一个对象时,不会无意中修改另一个对象。当你使用numpy时,这是一个折衷方案:减少计算机的工作量,增加程序员的工作量!