51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 899|回复: 0
打印 上一主题 下一主题

[转贴] Python是否支持复制字符串呢?

[复制链接]
  • TA的每日心情
    无聊
    3 小时前
  • 签到天数: 1052 天

    连续签到: 2 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-7-19 09:47:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    本文标题的问题分为两部分:(1)Python 中是否支持复制字符串?(2)如果不支持,为什么不支持?
      请读者花几分钟想一下,想清楚后,把你的答案记住,然后再往下看。
      让我们做一个约定(自愿遵守):如果看到最后,你推翻了现在的答案,建立了新的认知,这说明我写的内容有用,那请你任意赞赏,或者将本文分享给其他使用 Python 的小伙伴。
    1. 什么是复制字符串?
      首先,必须要大家对“复制”这个概念达成共识。复制,也叫拷贝,英文单词是 copy,具体意思是“将某事物通过某种方式制作成相同的一份或多份的行为”(释义来自维基百科)。复制的结果是,出现了多份极其相似但却相互独立的事物(副本),举例来说,你有一份文档 X,然后复制一份并重新命名为 Y,这两者是相互独立的,若你删除其中一个,另一个不会一起被删除。
      这个词用在 Python 里,我们想表达的是同样的意思,即复制行为会产生新的独立对象,它与原始对象极其相似,但两者的生命周期没有直接的关联关系。下面先用列表来举例:
    1. list1 = [1,2]
    2.   id(list1)
    3.   >>> 1981119454856
    4.   list2 = list1.copy()
    5.   print(list1 == list2)
    6.   >>> True
    7.   id(list2)
    8.   >>> 1981116983752
    复制代码
    上例中,列表 list2 是 list1 的副本,两者字面量相等,但是内存地址(即 id )不相等,是两个相互独立的对象。如果字符串能够做到同样的效果,那我们就说,字符串可以被复制,否则,我们说字符串不可以被复制。
      2. 怎样能复制字符串?
      有了上面的概念和示例,请先思考,你会用什么方式复制字符串呢?(暂停,思考3分钟)
      好了,先看看下面的几种方法:
    1. s0 = "Python编程学习圈"
    2.   s1 = s0
    3.   s2 = str(s0)
    4.   s3 = s0[:]
    5.   s4 = s0 + ''
    6.   s5 = '%s' % s0
    7.   s6 = s0 * 1
    8.   s7 = "".join(s0)
    9.   import copy
    10.   s8 = copy.copy(s0)
    复制代码
    你想到的复制方式是否在以上8种方式里呢?那么,如果把 s0 至 s8 的 id 打印出来,有哪些会跟 s0 不同呢?
      答案是,它们的内存地址 id 完全相同,也就是说,一顿操作猛如虎,结果却始终只有一份字符串,根本没有复制出新的字符串!
      会心一笑,这不就是因为字符串的 Intern 机制嘛,短字符串在内存中只会存在一份。
      但请别开心得太早,你可以把 s0 改成一个超长的字符串,例如:
    1.   s0 = "Python编程学习圈,正在学习Python,它的微信公众号也叫Python编程学习圈,欢迎你关注哦"
    复制代码
    然后,再重复上面的操作。最终,你会发现,s0 到 s8 的 id 还是完全相同。
      是不是吃惊了呢?新的 s0 明明已经超过 Intern 机制的长度了,为什么不会产生新的字符串呢?
      首先,请你相信,超出 Intern 机制的字符串可以存在多份,即你可以创建出值完全相同的多个字符串对象,因为字符串对象在内存中并不一定是唯一的:
    1. s9 = "Python编程学习圈,正在学习Python,它的微信公众号也叫Python编程学习圈,欢迎你关注哦"
    2.   print(id(s0) == id(s9))
    3.   >>> False
    复制代码
    上例表明,你可以创建出多个相同的字符串对象,但是这种方法与前面列举的8种不同,因为它是独立于 s0 的操作,并不是一种复制操作。从理论上讲,Python 完全可以提供一个方法,达到复制出新的副本的结果。现在的问题恰恰就是:为什么允许存在多个相等的字符串对象,但是却无法通过复制的方式来创建呢?
      为什么不允许复制字符串?
      我发现,不仅字符串不允许复制,元组也如此,事实上,还有 int 、float 也不支持复制。它们都是不可变对象,为什么不可变对象就不支持复制操作呢?
      在查资料的时候,我发现网上很多文章对于“不可变对象”的认识存在误区,这些人不知道 Intern 机制的存在,误以为字符串对象在内存只能有唯一一个,进而误以为不可变对象就是在内存中只有一份的对象。所以,这些文章很容易推断出错误的结论:因为字符串是不可变对象,所以字符串不支持复制。
      事实上,不可变对象跟复制操作之间,并没有必然的强相关的关系。肯定是出于别的原因,设计者才给不可变对象加上这种限制,这个原因是什么呢?
      在知乎上,有敏锐的同学提出了我的疑问“Python中如何复制一个值或字符串?”,可惜只有4个回答,而且都没答到点上。Stackoverflow上恰好也有一个问题“How can I copy a Python string?”,同样没多少人注意到,只有5个回答,好在最高票答案提到了一个点,即这样可以加快字典的查找速度。
      然而,他说的这个点并不靠谱。字典要求键值是可哈希对象,可是计算字符串的哈希值是根据字面值计算,所以对多个相等的字符串对象,其哈希值其实是一样的,对计算和查找根本无影响。
    1. w1 = "Python编程学习圈,正在学习Python,它的微信公众号也叫Python编程学习圈,欢迎你关注哦"
    2.   w2 = "Python编程学习圈,正在学习Python,它的微信公众号也叫Python编程学习圈,欢迎你关注哦"
    3.   print(w1 == w2)
    4.   >>> True
    5.   print(id(w1) == id(w2))
    6.   >>> False
    7.   print(hash(w1) == hash(w2))
    8.   >>> True
    复制代码
    继续查资料,终于在《流畅的Python》找到了明确的解释:
      这些细节是 CPython 核心开发者走的捷径和做的优化措施,对这门语言的用户而言无需了解,而且那些细节对其他 Python 实现可能没用,CPython 未来的版本可能也不会用。
      这本《流畅的Python》是进阶首选书目之一,我曾读过部分章节,没想到在一个不起眼的小节里,作者 “惊讶地发现” 元组的不可复制性,在此之前,他还自以为“对元组无所不知”,哈哈哈。
      虽然,我早猜测到原因是节省内存和提高速度,但看到这个明确的解释,知道这只是CPython 解释器的“善意的谎言”,而且在未来版本可能不会用,我感到特别意外。
      它证实了我的猜测,同时,也提供了超预期的信息:其它 Python 解释器可能支持复制不可变对象,目前 CPython 算是一种妥协,在未来可能会恢复不可变对象的复制操作呢!
      回到文章开头的两个问题,我们得到的答案是:Python 本身并不限制字符串的复制操作,只是当前版本的 CPython 做了优化,才导致出现这种“善意的谎言”,它这么做的原因为了对 Intern 机制做补充,设法使全部字符串对象在内存都只有一份,以达到节省内存的效果。
      CPython 是用 C 语言实现的 Python 解释器,是官方的、使用最广泛的解释器。除了它,还有用 [url=]Java[/url] 实现的 Jython 解释器、用 .NET 实现的 IronPython 解释器、用 Python 实现的 PyPy 解释器,等等。其它解释器都是怎么应对字符串的复制操作的呢?唉,学无止境,本人才疏学浅没有涉猎,还是先搁置疑问吧。
      这里,我就想提一个题外话,Python 最最最广为人诟病的就是 GIL(全局解释器锁),这导致它不支持真正意义的多线程,成为很多人指责 Python 慢的元凶。但是,这个问题是 CPython 解释器带来的,而像 Jython 解释器就不存在这个问题。






    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-27 12:54 , Processed in 0.065866 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表