简短的答案
切片列表不会生成列表中对象的副本;它只是复制对它们的引用。这是问题的答案。
长的答案
测试可变和不可变值
首先,让我们来测试基本的索赔。我们可以显示即使在不可变对象(如整数)的情况下,仅复制引用。这里有三个不同的整数对象,每个对象具有相同的值:
>>> 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 Pinsonault reminds us那样,这个开销就增加了。整数对象本身不是很大 – 它们比引用的大三倍。所以这样可以在绝对意义上节省一些记忆,但渐近地,可以将多个列表作为“视图”列入同一个内存可能是很好的。
通过使用视图节省内存
不幸的是,Python不提供简单的方法来生成列表中的“视图”对象。或者我应该说“幸运”!这意味着你不用担心切片来自哪里;改变原来不会影响切片。总体来说,这使得程序的行为更容易理解。
如果您真的想通过使用视图来节省内存,请考虑使用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时,这是权衡:计算机工作少,程序员工作更多!