引用还是复制
我刚开始学习Python 3。在一本Python书里,我看到说可以通过切片的方式强制解释器复制一个实例,而不是创建一个引用。
这段代码应该是创建了一个对已有实例s1的引用:
s1 = "Test"
s2 = s1
print(s1 == s2)
print(s1 is s2)
而这段代码应该是创建了一个新的实例:
s1 = "Test"
s2 = s1[:]
print(s1 == s2)
print(s1 is s2)
但是运行上面的例子后,两个结果都是一样的,都是指向s1的引用。有人能解释一下为什么和书里说的不一样吗?是我搞错了,还是书里的错误?
4 个回答
2
在Python中,字符串是不可变的。这意味着,一旦创建了一个字符串并把它存储在内存中,就不能再修改它了。每次你“修改”一个字符串时,其实是创建了一个新的副本。
你可能会注意到CPython的另一个有趣的行为:
s1 = 'Test'
s2 = 'Test'
id(s1) == id(s2)
>>> True
正如你所看到的,在CPython中,s1和s2指向内存中的同一个地址。
如果你需要一个可变对象(比如列表)的完整副本,可以查看标准库中的copy
模块。例如:
import copy
l1 = [1,2,3]
l2 = l1
id(l2) == id(l1) # a reference
>>> True
l3 = copy.deepcopy(l1)
id(l3) == id(l1)
>>> False
2
因为字符串是不可改变的,所以字符串的一个副本和指向这个字符串的新引用之间没有太大的区别。能够复制一个列表是很重要的,因为列表是可改变的,你可能想要改变一个列表而不影响另一个。但是字符串是不能改变的,所以复制字符串没有什么意义。
7
这对于像列表这样的可变数据类型是正确的(也就是说,像s1 = [1,2,3]
这样使用是符合预期的)。
但是在Python中,字符串和元组等是不可变的(这意味着它们不能被改变,你只能创建新的实例)。Python解释器没有理由去创建这些对象的副本,因为你不能通过s1去影响s2,反之亦然,你只能让s1或s2指向一个不同的字符串。