a[:] = b和a = b[:]有什么区别?(Python)
我在一次编程测试中被问到这个问题,但我不知道答案。有没有人能给点建议?
3 个回答
在这两种情况下,列表 a
最终都会变成列表 b
的一个副本。但实现这个过程的方法有所不同。
a[:] = b
是在修改列表 a
,让它的元素和 b
一样。
a = b[:]
则是创建了一个新的列表,这个新列表是 b
的副本,然后用这个新列表替换了 a
。
这两者的区别在于,我们是修改了一个已有的列表,还是创建了一个新的列表。
为了更好地理解这个区别:
a = range(3)
b = range(4)
c = a # c and a now share the same list
a[:] = b
print "a", a
print "b", b
print "C", c
这三种列表打印出来的结果是一样的。列表 c
和 a
共享同一个对象,所以当 a
被修改时,c
也会被修改。
a = range(3)
b = range(4)
c = a # c and a now share the same list
a = b[:]
print "a", a
print "b", b
print "C", c
现在,c
打印出来的结果就和 a
不一样了。因为在赋值之后,a
和 c
不再共享同一个对象。
从速度上看,a[:] = b
可能会比 a = b[:]
稍微快一点。因为第一种方式不需要创建一个新的列表对象,它只需修改已有的列表。这样做的一个好处是可以重用已经占用的内存,而不是重新分配新的内存。
a = b[:]
这行代码会调用 b
的 __getslice__
或 __getitem__
方法,然后把结果赋值给 a
。在绝大多数情况下(比如列表、元组和其他序列类型),这会创建一个“浅拷贝”,也就是说,a
会得到 b
的一个副本。我不知道有没有不这样做的类,但可能会有用户自定义的类型会有不同的表现。之前指向 a
的其他对象,仍然会指向旧的值。
而 a[:] = b
则是调用 __setslice__
或 __setitem__
方法,用 b
的元素替换 a
中的一部分元素。在这种情况下,如果 a
的序列类型表现正常,这将会替换掉整个 a
,因为 :
这个范围没有指定起止点,表示整个序列。这里的区别在于,不可变类型,比如元组,是不允许执行 __setslice__
的(例如会抛出 TypeError
异常)。之前指向 a
的其他对象也会被更新,因为底层对象正在被修改。
对于可变类型,比如 list
,a = b[:]
的结果和 a[:] = b
是一样的,a
会是 b
的一个浅拷贝;而对于不可变类型,比如 tuple
,a[:] = b
是无效的。对于表现不佳的用户自定义类型,情况就不一定了。还有一个区别是,之前指向同一个对象的其他对象会有什么变化——使用 a = b[:]
时,它们指向的是原始值(a
),而使用 a[:] = b
时,它们指向的是被修改后的对象(b
的浅拷贝)。
[:]
是一个切片操作符。
当它出现在左边时,它会直接替换列表里的内容,而不会新建一个列表的引用。
当它出现在右边时,它会复制一个和原列表内容相同的新列表。