摘要:本文標題的問題分為兩部分中是否支持復制字符串如果不支持,為什么不支持請讀者花幾分鐘想一下,想清楚后,把你的答案記住,然后再往下看。什么是復制字符串首先,必須要大家對復制這個概念達成共識。
連續幾篇文章都在寫 Python 字符串,這出乎我的意料了。但是,有的問題,不寫不行,特別是那種靈機一動想到的問題,最后你發現,很多人根本不懂卻又誤以為自己懂了。那就繼續刨根問底,探究個明白吧。
在上一篇文章《你真的知道Python的字符串怎么用嗎?》里,我突發奇想,將字符串跟列表做了比較,然后發現字符串竟然沒有復制的方法。當時沒有細想,只說要擱置疑問。過后,有好學的小伙伴在后臺留言,與我交流這個問題,給了我一些啟發。為了徹底弄懂它,我繼續查了不少資料,今天,就跟大家分享一下我發現的東西吧。
本文標題的問題分為兩部分:(1)Python 中是否支持復制字符串?(2)如果不支持,為什么不支持?
請讀者花幾分鐘想一下,想清楚后,把你的答案記住,然后再往下看。
讓我們做一個約定(自愿遵守):如果看到最后,你推翻了現在的答案,建立了新的認知,這說明我寫的內容有用,那請你任意贊賞,或者將本文分享給其他使用 Python 的小伙伴。
1. 什么是復制字符串?首先,必須要大家對“復制”這個概念達成共識。復制,也叫拷貝,英文單詞是 copy,具體意思是“將某事物通過某種方式制作成相同的一份或多份的行為”(釋義來自維基百科)。復制的結果是,出現了多份極其相似但卻相互獨立的事物(副本),舉例來說,你有一份文檔 X,然后復制一份并重新命名為 Y,這兩者是相互獨立的,若你刪除其中一個,另一個不會一起被刪除。
這個詞用在 Python 里,我們想表達的是同樣的意思,即復制行為會產生新的獨立對象,它與原始對象極其相似,但兩者的生命周期沒有直接的關聯關系。下面先用列表來舉例:
list1 = [1,2] id(list1) >>> 1981119454856 list2 = list1.copy() print(list1 == list2) >>> True id(list2) >>> 1981116983752
上例中,列表 list2 是 list1 的副本,兩者字面量相等,但是內存地址(即 id )不相等,是兩個相互獨立的對象。如果字符串能夠做到同樣的效果,那我們就說,字符串可以被復制,否則,我們說字符串不可以被復制。
2. 怎樣能復制字符串?有了上面的概念和示例,請先思考,你會用什么方式復制字符串呢?(暫停,思考3分鐘)
好了,先看看下面的幾種方法:
s0 = "Python貓" s1 = s0 s2 = str(s0) s3 = s0[:] s4 = s0 + "" s5 = "%s" % s0 s6 = s0 * 1 s7 = "".join(s0) import copy s8 = copy.copy(s0)
你想到的復制方式是否在以上8種方式里呢?那么,如果把 s0 至 s8 的 id 打印出來,有哪些會跟 s0 不同呢?
答案是,它們的內存地址 id 完全相同,也就是說,一頓操作猛如虎,結果卻始終只有一份字符串,根本沒有復制出新的字符串!
Python貓 的老讀者看到這,會心一笑,這不就是因為字符串的 Intern 機制嘛,短字符串在內存中只會存在一份,在《Python中的“特權種族”是什么?》這篇文章里提到過的。
但請別開心得太早,你可以把 s0 改成一個超長的字符串,例如:
s0 = "Python貓是來自喵星的客人,它喜歡地球和人類,正在學習Python,而且想借助Python變成人,它的微信公眾號也叫Python貓,歡迎你關注哦,喵喵喵喵~~~"
然后,再重復上面的操作。最終,你會發現,s0 到 s8 的 id 還是完全相同。
是不是吃驚了呢?新的 s0 明明已經超過 Intern 機制的長度了,為什么不會產生新的字符串呢?
首先,請你相信,超出 Intern 機制的字符串可以存在多份,即你可以創建出值完全相同的多個字符串對象,因為字符串對象在內存中并不一定是唯一的:
s9 = "Python貓是來自喵星的客人,它喜歡地球和人類,正在學習Python,而且想借助Python變成人,它的微信公眾號也叫Python貓,歡迎你關注哦,喵喵喵喵~~~" print(id(s0) == id(s9)) >>> False
上例表明,你可以創建出多個相同的字符串對象,但是這種方法與前面列舉的8種不同,因為它是獨立于 s0 的操作,并不是一種復制操作。從理論上講,Python 完全可以提供一個方法,達到復制出新的副本的結果。現在的問題恰恰就是:為什么允許存在多個相等的字符串對象,但是卻無法通過復制的方式來創建呢?
3. 為什么不允許復制字符串?我發現,不僅字符串不允許復制,元祖也如此,事實上,還有 int 、float 也不支持復制。它們都是不可變對象,為什么不可變對象就不支持復制操作呢?
在查資料的時候,我發現網上很多文章對于“不可變對象”的認識存在誤區,這些人不知道 Intern 機制的存在,誤以為字符串對象在內存只能有唯一一個,進而誤以為不可變對象就是在內存中只有一份的對象。所以,這些文章很容易推斷出錯誤的結論:因為字符串是不可變對象,所以字符串不支持復制。
事實上,不可變對象跟復制操作之間,并沒有必然的強相關的關系。肯定是出于別的原因,設計者才給不可變對象加上這種限制,這個原因是什么呢?
在知乎上,有敏銳的同學提出了我的疑問“Python中如何復制一個值或字符串?”,可惜只有4個回答,而且都沒答到點上。Stackoverflow上恰好也有一個問題“How can I copy a Python string?”,同樣沒多少人注意到,只有5個回答,好在最高票答案提到了一個點,即這樣可以加快字典的查找速度。
然而,他說的這個點并不靠譜。字典要求鍵值是可哈希對象,可是計算字符串的哈希值是根據字面值計算,所以對多個相等的字符串對象,其哈希值其實是一樣的,對計算和查找根本無影響。
w1 = "Python貓是來自喵星的客人,它喜歡地球和人類,正在學習Python,而且想借助Python變成人,它的微信公眾號也叫Python貓,歡迎你關注哦,喵喵喵喵~~~" w2 = "Python貓是來自喵星的客人,它喜歡地球和人類,正在學習Python,而且想借助Python變成人,它的微信公眾號也叫Python貓,歡迎你關注哦,喵喵喵喵~~~" print(w1 == w2) >>> True print(id(w1) == id(w2)) >>> False print(hash(w1) == hash(w2)) >>> True
繼續查資料,終于在《流暢的Python》找到了明確的解釋:
這些細節是 CPython 核心開發者走的捷徑和做的優化措施,對這門語言的用戶而言無需了解,而且那些細節對其他 Python 實現可能沒用,CPython 未來的版本可能也不會用。
這本《流暢的Python》是進階首選書目之一,我曾讀過部分章節,沒想到在一個不起眼的小節里,作者 “驚訝地發現” 元祖的不可復制性,在此之前,他還自以為“對元祖無所不知”,哈哈哈。
雖然,我早猜測到原因是節省內存和提高速度,但看到這個明確的解釋,知道這只是CPython 解釋器的“善意的謊言”,而且在未來版本可能不會用,我感到特別意外。
它證實了我的猜測,同時,也提供了超預期的信息:其它 Python 解釋器可能支持復制不可變對象,目前 CPython 算是一種妥協,在未來可能會恢復不可變對象的復制操作呢!
回到文章開頭的兩個問題,我們得到的答案是:Python 本身并不限制字符串的復制操作,只是當前版本的 CPython 做了優化,才導致出現這種“善意的謊言”,它這么做的原因為了對 Intern 機制做補充,設法使全部字符串對象在內存都只有一份,以達到節省內存的效果。
CPython 是用 C 語言實現的 Python 解釋器,是官方的、使用最廣泛的解釋器。除了它,還有用 Java 實現的 Jython 解釋器、用 .NET 實現的 IronPython 解釋器、用 Python 實現的 PyPy 解釋器,等等。其它解釋器都是怎么應對字符串的復制操作的呢?唉,學無止境,本人才疏學淺沒有涉獵,還是先擱置疑問吧。
這里,我就想提一個題外話,Python 最最最廣為人詬病的就是 GIL(全局解釋器鎖),這導致它不支持真正意義的多線程,成為很多人指責 Python 慢的元兇。但是,這個問題是 CPython 解釋器帶來的,而像 Jython 解釋器就不存在這個問題。
好了,就此打住吧。你是否還記得在文章開頭時想到的答案呢?是否改變了最初的想法呢?歡迎關注公眾號 Python貓 ,來跟我交流,一起來學習 Python ,做個合格的 Pythonista。
參考學習:
《流暢的Python》
https://www.zhihu.com/questio...
https://dwz.cn/4o0WXy8G
最后是福利時刻:本公眾號(Python貓)由清華大學出版社贊助,將抽獎送出兩本新書《深入淺出Python機器學習》,截止時間到11月29日18:18,點擊 這個鏈接,馬上參與吧。
本文原創并首發于微信公眾號【Python貓】,后臺回復“愛學習”,免費獲得20+本精選電子書。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42702.html
摘要:本文標題的問題分為兩部分中是否支持復制字符串如果不支持,為什么不支持請讀者花幾分鐘想一下,想清楚后,把你的答案記住,然后再往下看。什么是復制字符串首先,必須要大家對復制這個概念達成共識。 showImg(https://segmentfault.com/img/bVbkgbP?w=4147&h=2765); 連續幾篇文章都在寫 Python 字符串,這出乎我的意料了。但是,有的問題,不...
閱讀 3463·2021-11-25 09:43
閱讀 1062·2021-11-15 11:36
閱讀 3313·2021-11-11 16:54
閱讀 3974·2021-09-27 13:35
閱讀 4364·2021-09-10 11:23
閱讀 5675·2021-09-07 10:22
閱讀 3032·2021-09-04 16:40
閱讀 769·2021-08-03 14:03