溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python語言中計(jì)數(shù)方法的演變過程是怎么樣的

發(fā)布時(shí)間:2021-10-28 16:49:54 來源:億速云 閱讀:103 作者:柒染 欄目:編程語言

Python語言中計(jì)數(shù)方法的演變過程是怎么樣的,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

有時(shí)候,利用Python語言簡潔、優(yōu)雅地解決問題的方法,會隨著時(shí)間變化。隨著Python不斷進(jìn)化,統(tǒng)計(jì)列表元素?cái)?shù)量的方法也在改變。

以計(jì)算元素在列表中出現(xiàn)的次數(shù)為例,我們可以編寫出許多不同的實(shí)現(xiàn)方法。在分析這些方法時(shí),我們先不關(guān)注性能,只考慮代碼風(fēng)格。

要理解這些不同的實(shí)現(xiàn)方式,我們得先知道一些歷史背景。幸運(yùn)的是,我們生活在"future"世界,擁有一臺時(shí)間機(jī)器。接下來,我們一起坐上時(shí)光機(jī),回到1997年吧。

if 語句

1997年1月1日,我們使用的是Python 1.4?,F(xiàn)在有一個(gè)不同顏色組成的列表,我們想知道列表里每種顏色出現(xiàn)的次數(shù)。我們用字典來計(jì)算吧!

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors:     if color_counts.has_key(c):         color_counts[c] = color_counts[c] + 1     else:         color_counts[c] = 1

注意:我們沒有使用+=,因?yàn)樵隽抠x值直到Python 2.0才出現(xiàn);另外,我們也沒有使用c in color_counts這個(gè)慣用法(idiom),因?yàn)檫@也是Python 2.2中才發(fā)明的,

運(yùn)行上述代碼之后,我們會發(fā)現(xiàn)color_counts字典里,現(xiàn)在包含了列表中每種顏色的出現(xiàn)次數(shù)。

>>> color_counts
{'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1}

上面的實(shí)現(xiàn)很簡單。我們遍歷了每一種顏色,并判斷該顏色是否在字典中。如果不在,就在字典加入該顏色;如果在,就增加這種顏色的計(jì)數(shù)。

我們還可以把上面的代碼改寫為:

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors:     if not color_counts.has_key(c):         color_counts[c] = 0     color_counts[c] = color_counts[c] + 1

如果列表稀疏度高(即列表中不重復(fù)的顏色數(shù)量很多),這段代碼可能運(yùn)行的會有點(diǎn)慢。因?yàn)槲覀儸F(xiàn)在要執(zhí)行兩個(gè)語句,而不是一個(gè)。但是我們不關(guān)心性能問題,我們只關(guān)注編碼風(fēng)格。經(jīng)過思考,我們決定采用新版的代碼。

try代碼塊(Code Block)

1997年1月2日,我們使用的還是Python 1.4。今早醒來的時(shí)候,我們突然意識到:我們的代碼遵循的是“三思而后行”(Look  Before You Leap,即事先檢查每一種可能出現(xiàn)的情況)原則,但實(shí)際上我們應(yīng)該按照“獲得諒解比獲得許可容易”(Easier to Ask  Forgiveness, Than Permission,即不檢查,出了問題由異常處理來處理)的原則進(jìn)行編程,因?yàn)楹笳吒雍啙?、?yōu)雅。我們用try-except代碼塊來重構(gòu)下代碼吧:

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors:     try:         color_counts[c] = color_counts[c] + 1     except KeyError:         color_counts[c] = 1

現(xiàn)在,我們的代碼嘗試增加每種顏色的計(jì)數(shù)。如果某顏色不在字典里,那么就會拋出KeyError,我們隨之將該顏色的計(jì)數(shù)設(shè)置為1。

get方法

1998年1月1日,我們已經(jīng)升級到了Python 1.5。我們決定重構(gòu)之前的代碼,使用字典中新增的get方法。

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors:     color_counts[c] = color_counts.get(c, 0) + 1

現(xiàn)在,我們的代碼會遍歷每種顏色,從字典中獲取該顏色的當(dāng)前計(jì)數(shù)值。如果沒有這個(gè)計(jì)數(shù)值,則該顏色的計(jì)數(shù)值默認(rèn)為0,然后在數(shù)值的基礎(chǔ)上加1。***將字典中相應(yīng)鍵的值設(shè)置為新的計(jì)數(shù)。

把主要代碼都寫在一行里,感覺很酷,但是我們不敢完全肯定這種做法更加簡潔、優(yōu)雅。我們覺得可能有點(diǎn)太聰敏了,所以還是撤銷了這次的重構(gòu)。

setdefault方法

2001年1月1日,我們現(xiàn)在使用的是Python 2.0。我們聽說字典類型現(xiàn)在有一個(gè)setdefault方法,決定利用它重構(gòu)我們的代碼。我們還決定使用新增加的+=增量賦值運(yùn)算符。

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors:     color_counts.setdefault(c, 0)     color_counts[c] += 1

無論是否需要,我們在每一次循環(huán)時(shí)都會調(diào)用setdefault方法。但這樣做,的確會讓代碼看上去可讀性更高。我們發(fā)現(xiàn)這種方法比之前的代碼更加簡潔、優(yōu)雅,所以提交了此次修改。

fromkeys方法

2004年1月1日,我們使用的是Python 2.3。我們聽說字典新增了一個(gè)叫fromkeys的類方法(class method),可以利用列表中的元素作為鍵來構(gòu)建字典。我們使用新方法重構(gòu)了代碼:

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = dict.fromkeys(colors, 0) for c in colors:     color_counts[c] += 1

這段代碼將不同的顏色作為鍵,創(chuàng)建了一個(gè)新的字典,每個(gè)鍵的值被默認(rèn)設(shè)置為0。這樣,我們增加每個(gè)鍵的值時(shí),就不用擔(dān)心是否已經(jīng)進(jìn)行了設(shè)置。我們也不需要在代碼中進(jìn)行檢查或異常處理了,這看上去是個(gè)改進(jìn)。我們決定就這樣修改代碼。

推導(dǎo)式(Comprehension)與集合

2005年1月1日,我們現(xiàn)在用的是Python 2.4。我們發(fā)現(xiàn)可以利用集合(Python  2.3中發(fā)布,2.4版成為內(nèi)置類型)與列表推導(dǎo)式(Python 2.0中發(fā)布)來解決計(jì)數(shù)問題。進(jìn)一步思考之后,我想起來Python  2.4中還發(fā)布了生成器表達(dá)式(generator expressions),我們***決定不用列表推導(dǎo)式,而是采用生成器表達(dá)式。

colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = dict((c, colors.count(c)) for c in set(colors))

注意:我們這里使用的不是字典推導(dǎo)式,因?yàn)樽值渫茖?dǎo)式直到Python 2.7才被發(fā)明。

運(yùn)行成功了,而且只有一行代碼。但是這種代碼夠簡潔、優(yōu)雅嗎 ?

我們想起了Python之禪(Zen of Python),這個(gè)Python編程指導(dǎo)原則起源于一個(gè)Python郵件列表,并悄悄地收進(jìn)了Python 2.2.1版本中。我們在REPL(read-eval-print loop,交互式解釋器)界面中輸入import this

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly. # 優(yōu)美勝過丑陋
Explicit is better than implicit. # 明確勝過含蓄
Simple is better than complex. # 簡單勝過復(fù)雜
Complex is better than complicated. # 復(fù)雜勝過難懂
Flat is better than nested. # 扁平勝過嵌套
Sparse is better than dense. # 稀疏勝過密集
Readability counts. # 易讀亦有價(jià)
Special cases aren't special enough to break the rules. # 特例也不能特殊到打破規(guī)則
Although practicality beats purity. # 盡管實(shí)用會擊敗純潔
Errors should never pass silently. # 錯(cuò)誤永遠(yuǎn)不應(yīng)默默地溜掉
Unless explicitly silenced. # 除非明確地使其沉默
In the face of ambiguity, refuse the temptation to guess.  # 面對著不確定,要拒絕猜測的誘惑
There should be one-- and preferably only one --obvious way to do it.  # 應(yīng)該有一個(gè)–寧肯只有一個(gè)–明顯的實(shí)現(xiàn)方法
Although that way may not be obvious at first unless you're Dutch. # 也許這個(gè)方法開始不是很明顯,除非你是荷蘭人
Now is better than never. #現(xiàn)在做也要?jiǎng)龠^不去做
Although never is often better than right now. # 盡管不做通常好過立刻做
If the implementation is hard to explain, it's a bad idea. # 如果實(shí)現(xiàn)很難解釋,那它就是一個(gè)壞想法
If the implementation is easy to explain, it may be a good idea. # 如果實(shí)現(xiàn)容易解釋,那它可能就是一個(gè)好想法
Namespaces are one honking great idea -- let's do more of those! # 命名空間是一個(gè)響亮的出色想法就讓我們多用用它們

我們的代碼變得更復(fù)雜,時(shí)間復(fù)雜度從O(n)增加到了O(n2);還變的更丑,可讀性更差了。我們那樣改,是一次有趣的嘗試,但是一行代碼的實(shí)現(xiàn)形式,沒有我們之前的方法簡潔、優(yōu)雅。我們***還是決定撤銷修改。

defaultdict方法

2007年1月1日,我們使用的是Python 2.5。我們剛發(fā)現(xiàn),defaultdict已經(jīng)被加入標(biāo)準(zhǔn)庫。這樣,我們就可以把字典的默認(rèn)鍵值設(shè)置為0了。讓我們使用defaultdict重構(gòu)代碼:

from collections import defaultdict
    colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = defaultdict(int) for c in colors:         color_counts[c] += 1

那個(gè)for循環(huán)現(xiàn)在變得真簡單!這樣肯定是更加簡潔、優(yōu)雅了。

我們發(fā)現(xiàn),color_counts這個(gè)變量的行為現(xiàn)在有點(diǎn)不同,但是它的確繼承了字典的特性,支持所有相同的映射功能。

>>> color_counts
defaultdict(<type 'int'>, {'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1})

我們在這里沒有把color_counts轉(zhuǎn)換成字典,而是假設(shè)其他的代碼也使用鴨子類型(duck typing, Python中動態(tài)類型的一種,這里的意思是:其他代碼會將color_counts視作字典類型),不再改動這個(gè)類似字典的對象。

Counter

2011年1月1日,我們使用的是Python 2.7。別人告訴我們,之前使用defaultdict編寫的代碼,不再是統(tǒng)計(jì)顏色出現(xiàn)次數(shù)最簡潔、優(yōu)雅的方法了。Python 2.7中新引入了一個(gè)Counter類,可以完全解決我們的問題。

from collections import Counter
colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = Counter(colors)

還有比這更簡單的方法嗎?這個(gè)一定是最簡潔、優(yōu)雅的實(shí)現(xiàn)了。

defaultdict一樣,Counter類返回的也是一個(gè)類似字典的對象(實(shí)際是字典的一個(gè)子類)。這對滿足我們的需求來說足夠了,所以我們就這么干了。

>>> color_counts
Counter({'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1})

性能考慮

請注意,在編寫這些實(shí)現(xiàn)方式時(shí),我們都沒有關(guān)注效率問題。大部分方法的時(shí)間復(fù)雜度相同(O(n)),但是不同的Python語言實(shí)現(xiàn)形式(如CPython, PyPy,或者Jython)下,運(yùn)行時(shí)間會有差異。

盡管性能不是我們的主要關(guān)注點(diǎn),我還是在CPython 3.5.0的實(shí)現(xiàn)下測試了運(yùn)行時(shí)間。從中,你會發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:隨著列表中顏色元素的密度(即相同元素的數(shù)量)變化,每一種實(shí)現(xiàn)方法的相對效率也會不同。

根據(jù)Python之禪,“ 應(yīng)該有一個(gè)&mdash;&mdash;寧肯只有一個(gè)明顯的實(shí)現(xiàn)方法”。這句話所說的狀態(tài)值得追求,但事實(shí)是,并不總是只有一種明顯的方法。這個(gè)“明顯”的方法會隨著時(shí)間、需求和專業(yè)水平,不斷地變化。

看完上述內(nèi)容,你們掌握Python語言中計(jì)數(shù)方法的演變過程是怎么樣的的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI