国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

join()方法的神奇用處與Intern機(jī)制的軟肋

Towers / 851人閱讀

摘要:機(jī)制失效的情況方法的神奇用處使我不得不改變對(duì)機(jī)制的認(rèn)識(shí),本小節(jié)就帶大家重新學(xué)習(xí)一下機(jī)制吧。目前看來,方法最具通用性。學(xué)習(xí)的方法論總而言之,因?yàn)橹匦聦W(xué)習(xí)方法的神奇用處與機(jī)制的例外情況,我得以修正上篇文章的錯(cuò)誤。


上篇文章《Python是否支持復(fù)制字符串呢?》剛發(fā)出一會(huì),@發(fā)條橙 同學(xué)就在后臺(tái)留言,指出了一處錯(cuò)誤。我一驚,馬上去驗(yàn)證,竟然真的錯(cuò)了,而且在完全沒意料到的地方!我開始以為只是疏漏,一細(xì)想,發(fā)現(xiàn)不簡(jiǎn)單,遇到了百思不得其解的問題了。所以,這篇文章還得再聊聊字符串。

照例先總結(jié)下本文內(nèi)容:(1)join() 方法除了在拼接字符串時(shí)速度較快,它還是目前看來最通用有效的復(fù)制字符串的方法 (2)Intern 機(jī)制(字符串滯留)并非萬能的,本文探索一下它的軟肋有哪些

1. join()方法不止是拼接

我先把那個(gè)問題化簡(jiǎn)一下吧:

ss0 = "hi"
ss1 = "h" + "i"
ss2 = "".join(ss0)

print(ss0 == ss1 == ss2) >>> True
print(id(ss0) == id(ss1)) >>> True
print(id(ss0) == id(ss2)) >>> False

上面代碼中,奇怪的地方就在于 ss2 竟然是一個(gè)獨(dú)立的對(duì)象!按照最初想當(dāng)然的認(rèn)知,我認(rèn)定它會(huì)被 Intern 機(jī)制處理掉,所以是不會(huì)占用獨(dú)立內(nèi)存的。上篇文章快寫完的時(shí)候,我突然想到 join 方法,所以沒做驗(yàn)證就臨時(shí)加進(jìn)去,導(dǎo)致了意外的發(fā)生。

按照之前在“特權(quán)種族”那篇文章的總結(jié),我對(duì)字符串 Intern 機(jī)制有這樣的認(rèn)識(shí):

Python中,字符串使用Intern機(jī)制實(shí)現(xiàn)內(nèi)存地址共用,長(zhǎng)度不超過20,且僅包括下劃線、數(shù)字、字母的字符串才會(huì)被intern;涉及字符串拼接時(shí),編譯期優(yōu)化結(jié)果會(huì)與運(yùn)行期計(jì)算結(jié)果不同。

為什么 join 方法拼接字符串時(shí),可以不受 Intern 機(jī)制作用呢?

回看那篇文章,發(fā)現(xiàn)可能存在編譯期與運(yùn)行期的差別!

# 編譯對(duì)字符串拼接的影響
s1 = "hell"
s2 = "hello"
"hell" + "o" is s2 
>>>True
s1 + "o" is s2 
>>>False
# "hell" + "o"在編譯時(shí)變成了"hello",
# 而s1+"o"因?yàn)閟1是一個(gè)變量,在運(yùn)行時(shí)才拼接,所以沒有被intern

實(shí)驗(yàn)一下,看看:

# 代碼加上
ss3 = "".join("hi")
print(id(ss0) == id(ss3)) >>> False

ss3 仍然是獨(dú)立對(duì)象,難道這種寫法還是在運(yùn)行期時(shí)拼接?那怎么判斷某種寫法在編譯期還是在運(yùn)行期起作用呢?繼續(xù)實(shí)驗(yàn):

s0 = "Python貓"
import copy
s1 = copy.copy(s0)
s2 = copy.copy("Python貓")

print(id(s0) == id(s1))
>>> True
print(id(s0) == id(s2))
>>> False

看來,不能通過是否顯性傳值來判斷。

那就只能從 join 方法的實(shí)現(xiàn)原理入手查看了。經(jīng)某交流群的小伙伴提醒,可以去 Python Tutor 網(wǎng)站,看看可視化執(zhí)行過程。但是,很遺憾,也沒看出什么底層機(jī)制。

我找了分析 CPython 源碼的資料(含上期薦書欄目的《Python源碼剖析》)來學(xué)習(xí),但是,這些資料只比較 join() 方法與 + 號(hào)拼接法在原理與使用內(nèi)存上的差異,并沒提及為何 Intern 機(jī)制對(duì)前者會(huì)失效,而對(duì)后者卻是生效的。

現(xiàn)象已經(jīng)產(chǎn)生,我只能暫時(shí)解釋說,join 方法會(huì)不受 Intern 機(jī)制控制,它有獨(dú)享內(nèi)存的“特權(quán)”。

那就是說,其實(shí)有復(fù)制字符串的方法!上篇《Python是否支持復(fù)制字符串呢?》由于沒有發(fā)現(xiàn)這點(diǎn),最后得出了錯(cuò)誤的結(jié)論!

由于這個(gè)特例,我要修改上篇文章的結(jié)論了:Python 本身并不限制字符串的復(fù)制操作,CPython 解釋器出于優(yōu)化性能的考慮,加入了一些小把戲,試圖使字符串對(duì)象在內(nèi)存中只有一份,盡管如此,仍存在有效復(fù)制字符串的方法,那就是 join() 方法。

2. Intern 機(jī)制失效的情況

join() 方法的神奇用處使我不得不改變對(duì) Intern 機(jī)制的認(rèn)識(shí),本小節(jié)就帶大家重新學(xué)習(xí)一下 Intern 機(jī)制吧。

所謂 Intern 機(jī)制,即字符串滯留(string interning),它通過維護(hù)一個(gè)字符串常量池(string intern pool),從而試圖只保存唯一的字符串對(duì)象,達(dá)到既高效又節(jié)省內(nèi)存地處理字符串的目的。在創(chuàng)建一個(gè)新的字符串對(duì)象后,Python 先比較常量池中是否有相同的對(duì)象(interned),有的話則將指針指向已有對(duì)象,并減少新對(duì)象的指針,新對(duì)象由于沒有引用計(jì)數(shù),就會(huì)被垃圾回收機(jī)制回收掉,釋放出內(nèi)存。

Intern 機(jī)制不會(huì)減少新對(duì)象的創(chuàng)建與銷毀,但最終會(huì)節(jié)省出內(nèi)存。這種機(jī)制還有另一個(gè)好處,即被 Interned 的相同字符串作比較時(shí),幾乎不花時(shí)間。實(shí)驗(yàn)數(shù)據(jù)如下(資料來源:http://t.cn/ELu9n7R):

Intern 機(jī)制的大致原理很好理解,然而影響結(jié)果的還有 CPython 解釋器的其它編譯及運(yùn)行機(jī)制,字符串對(duì)象受到這些機(jī)制的共同影響。實(shí)際上,只有那些“看起來像” Python 標(biāo)識(shí)符的字符串才會(huì)被處理。源代碼StringObject.h的注釋中寫道:

/ … … This is generally restricted to strings that “l(fā)ooklike” Python identifiers, although the intern() builtin can be used to force interning of any string … … /

這些機(jī)制的相互作用,不經(jīng)意間帶來了不少混亂的現(xiàn)象:

# 長(zhǎng)度超過20,不被intern VS 被intern
"a" * 21 is "aaaaaaaaaaaaaaaaaaaaa"
>>> False
"aaaaaaaaaaaaaaaaaaaaa" is "aaaaaaaaaaaaaaaaaaaaa"
>>> True

# 長(zhǎng)度不超過20,不被intern VS 被intern
s = "a"
s * 5 is "aaaaa"
>>> False
"a" * 5 is "aaaaa"
>>> True


# join方法,不被intern VS 被intern
"".join("hi") is "hi"
>>> False
"".join("h") is "h"
>>> True

# 特殊符號(hào),不被intern VS 被"intern"
"python!" is "python!"
>>> False
a, b = "python!", "python!"
a is b
>>> True

這些現(xiàn)象當(dāng)然都能被合理解釋,然而由于不同機(jī)制的混合作用,就很容易造成誤會(huì)。比如第一個(gè)例子,很多介紹 Intern 機(jī)制的文章在比較出 "a" * 21 的id有變化后,就認(rèn)為 Intern 機(jī)制只對(duì)長(zhǎng)度不超過20的字符串生效,可是,當(dāng)看到長(zhǎng)度超過20的字符串的id還相等時(shí),這個(gè)結(jié)論就變錯(cuò)誤了。當(dāng)加入常量合并(Constant folding) 的機(jī)制后,長(zhǎng)度不超過20的字符串會(huì)被合并的現(xiàn)象才得到解釋。可是,在 CPython 的源碼中,只有長(zhǎng)度不超過1字節(jié)的字符串才會(huì)被 intern ,為何長(zhǎng)度超標(biāo)的情況也出現(xiàn)了呢? 再加入 CPython 的編譯優(yōu)化機(jī)制,才能解釋。

所以,看似被 intern 的兩個(gè)字符串,實(shí)際可能不是 Intern 機(jī)制的結(jié)果,而是其它機(jī)制的結(jié)果。同樣地,看似不能被 intern 的兩個(gè)字符串,實(shí)際可能被其它機(jī)制以類似方式處理了。

如此種種,便提高了理解 Intern 機(jī)制的難度。

就我在上篇文章中所關(guān)心的“復(fù)制字符串”話題而言,只有當(dāng) Intern 機(jī)制與其它這些機(jī)制統(tǒng)統(tǒng)失效時(shí),才能做到復(fù)制字符串。目前看來,join 方法最具通用性。

3. 學(xué)習(xí)的方法論

總而言之,因?yàn)橹匦聦W(xué)習(xí) join 方法的神奇用處與 Intern 機(jī)制的例外情況,我得以修正上篇文章的錯(cuò)誤。在此過程中,我得到了新的知識(shí),以及思考學(xué)習(xí)的樂趣。

《超人》電影中有一句著名的臺(tái)詞,在今年上映的《頭號(hào)玩家》中也出現(xiàn)了:

有的人從《戰(zhàn)爭(zhēng)與和平》里看到的只是一個(gè)普通的冒險(xiǎn)故事,

有的人則能通過閱讀口香糖包裝紙上的成分表來解開宇宙的奧秘。

我讀到的是一種敏銳思辨的思想、孜孜求索的態(tài)度和以小窺大的方法。作為一個(gè)低天賦的人,受此鼓舞,我會(huì)繼續(xù)追問那些看似沒意義的問題(“如何刪除字符串”、“如何復(fù)制字符串”...),一點(diǎn)一點(diǎn)地學(xué)習(xí) Python ,以我的方式理解它。同時(shí),希望能給我的讀者們帶來一些收獲。

PS.不少人在期待 “Python貓” 系列,別急哈,讓那只貓?jiān)偎瘞滋欤人褋恚姨娲蠹掖咚?/p>

字符串系列文章:

詳解Python拼接字符串的七種方式

你真的知道Python的字符串是什么嗎?

你真的知道Python的字符串怎么用嗎?

Python是否支持復(fù)制字符串呢?

Python貓系列:

有了Python,我能叫出所有貓的名字

Python對(duì)象的身份迷思:從全體公民到萬物皆數(shù)

-----------------

本文原創(chuàng)并首發(fā)于微信公眾號(hào)【Python貓】,后臺(tái)回復(fù)“愛學(xué)習(xí)”,免費(fèi)獲得20+本精選電子書。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/25056.html

相關(guān)文章

  • join()方法神奇用處Intern機(jī)制軟肋

    摘要:機(jī)制失效的情況方法的神奇用處使我不得不改變對(duì)機(jī)制的認(rèn)識(shí),本小節(jié)就帶大家重新學(xué)習(xí)一下機(jī)制吧。目前看來,方法最具通用性。學(xué)習(xí)的方法論總而言之,因?yàn)橹匦聦W(xué)習(xí)方法的神奇用處與機(jī)制的例外情況,我得以修正上篇文章的錯(cuò)誤。 showImg(https://segmentfault.com/img/bVbkpfb?w=3106&h=2071);上篇文章《Python是否支持復(fù)制字符串呢?》剛發(fā)出一會(huì),...

    yck 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<