本文标题中的问题分为两部分:(1)Python是否支持复制字符串?(2)如果不支持,为什么不支持?请读者花几分钟思考一下,思考清楚后,记住你的答案,然后继续阅读。许个诺言(自愿遵守):如果你看到最后,你推翻了现在的答案,建立了新的认知,说明我写的内容有用,那么请随意欣赏,或者把这篇文章分享给其他网友Python的小伙伴。1、什么是复制字符串?首先,大家要对“复制”这个概念达成共识。复制,也叫拷贝,英文单词copy,具体意思是“以某种方式将某物制作一份或多份的行为”(定义来自维基百科)。复制的结果是有很多非常相似但独立的东西(副本)。比如你有一个文档X,然后复制重命名为Y,两者是相互独立的。如果您删除其中一个,另一个将不会一起删除。当这个词在Python中使用时,我们指的是同一件事,即复制行为会产生一个新的独立对象,该对象与原始对象非常相似,但其生命周期并不直接相关。我们先以列表为例:list1=[1,2]id(list1)>>>1981119454856list2=list1.copy()print(list1==list2)>>>Trueid(list2)>>>1981116983752In上面的例子,列表list2是list1的副本,两者字面量相等,但内存地址(即id)不相等,是两个独立的对象。如果字符串能达到同样的效果,那么我们就说这个字符串可以被复制,否则我们就说这个字符串不能被复制。2.如何复制一个字符串?有了上面的概念和例子,请先想一想,你会如何复制一个字符串?(停顿,思考3分钟)嗯,先看下面的方法:s0=《Python编程学习圈》s1=s0s2=str(s0)s3=s0[:]s4=s0+''s5='%s'%s0s6=s0*1s7="".join(s0)importcopies8=copy.copy(s0)以上8种复制方式你想到了吗?那么,如果打印出s0到s8的id,哪些会和s0不一样呢?答案是它们的内存地址id完全一样,也就是说操作猛如虎,结果却总是只有一份字符串,根本没有复制新的字符串!会心一笑,这不是因为字符串的Intern机制,短字符串在内存中只会有一份,不过大家也别高兴得太早,可以把s0换成超长字符串,例如:s0="Python编程学习圈,学习Python,其微信公众号又叫Python编程学习圈,欢迎关注。"然后,重复上述操作。最终你会发现s0到s8的id还是一模一样。你惊喜吗?新的s0明显超过了Intern机制的长度,那为什么不生成新的字符串呢?首先请相信,除了Intern机制之外,字符串可以存在多份副本,即可以创建多个值完全相同的字符串对象,因为字符串对象在内存中不一定是唯一的:s9="Python编程学习圈子,学习Python,其微信公众号又叫Python编程学习圈,欢迎关注"print(id(s0)==id(s9))>>>False上面的例子说明可以创建多个相同的字符串对象,但是这个方法与上面列出的八个不同,因为它是独立于s0的操作,而不是复制操作。理论上,Python可以提供一种方法来实现复制一个新副本的结果。现在的问题恰恰是:为什么允许多个相等的字符串对象,却不能通过复制创建?**为什么不允许复制字符串?**我发现不仅字符串不允许复制,元组也是。事实上,int和float也不支持复制。都是不可变对象,为什么不可变对象不支持复制操作呢?查资料的时候发现网上很多文章对“不可变对象”都有误解。这些人不知道Intern机制的存在,误认为内存中只能有一个string对象,进而误认为immutableobjects是内存中只有一份的对象。所以,这些文章很容易得出错误的结论:因为字符串是不可变对象,所以字符串不支持复制。事实上,不可变对象和复制操作之间没有必然的强关系。一定是出于其他原因,设计者对不可变对象施加了这种限制。是什么原因?在知乎上,有热心的同学提出了我的问题“如何在Python中复制一个值或字符串?”不幸的是,只有4个答案,没有一个回答到重点。也恰好有一个问题“如何复制Python字符串?”在计算器上。没有多少人注意到它,只有5个答案。好在得票最高的答案提到了一点,就是可以加快查字典的速度。不过,他说的这一点并不靠谱。字典要求key值是一个hashable对象,但是字符串的hash值是根据字面值计算的,所以对于多个相等的string对象,hash值其实是一样的,对计算和查找没有影响.w1="Python编程学习圈,学习Python,其微信公众号又叫Python编程学习圈,欢迎关注"w2="Python编程学习圈,学习Python,其微信公众号又叫Python编程学习圈子,欢迎大家关注"print(w1==w2)>>>Trueprint(id(w1)==id(w2))>>>Falseprint(hash(w1)==hash(w2))>>>True继续查找资料,终于在《流畅的Python》中找到了明确的解释:这些细节是CPython核心开发者采取的捷径和优化措施,该语言的用户不需要了解,那些细节对其他人来说很重要Python实现可能无法正常工作,未来版本的CPython也可能无法正常工作。这篇《流畅的Python》是进修的首选参考书目之一。看了一些章节,没想到在一个不起眼的章节,作者“惊奇地发现”了元组的不可复现性。在此之前,他自己都认为“元组无所不知”,哈哈哈。虽然,我猜想是为了节省内存和提高速度,但看到这个明明白白的解释还是很惊讶,知道这只是CPython解释器的一个“善意的谎言”,以后的版本可能不会使用。它证实了我的猜测,同时也提供了意想不到的信息:其他Python解释器可能支持复制不可变对象。目前CPython算是妥协了,以后可能会恢复不可变对象的复制操作!回到文章开头的两个问题,我们得到的答案是:Python本身并没有限制对字符串的复制操作,而是当前版本的CPython进行了优化,才导致了这个“善意的谎言”。原因是为了补充Intern机制,尽量让所有的string对象在内存中只有一份,达到节省内存的效果。CPython是用C语言实现的Python解释器,是官方最广泛使用的解释器。除此之外,还有用Java实现的Jython解释器,用.NET实现的IronPython解释器,用Python实现的PyPy解释器等等。其他解释器如何处理字符串复制操作?唉,学无止境。本人知之甚少,没有涉足,暂且放下疑惑。在这里,我想提一个题外话。Python最受诟病的就是GIL(GlobalInterpreterLock),这导致它不支持真正的多线程,成为很多人指责Python慢??的罪魁祸首。不过这个问题是CPython解释器带来的,Jython解释器没有这个问题。以上就是本次分享的全部内容。想了解更多python知识,请前往公众号:Python编程学习圈,发“J”免费领取,每日干货分享
