您好,登錄后才能下訂單哦!
Python語言中計數方法的演變過程是怎么樣的,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
有時候,利用Python語言簡潔、優雅地解決問題的方法,會隨著時間變化。隨著Python不斷進化,統計列表元素數量的方法也在改變。
以計算元素在列表中出現的次數為例,我們可以編寫出許多不同的實現方法。在分析這些方法時,我們先不關注性能,只考慮代碼風格。
要理解這些不同的實現方式,我們得先知道一些歷史背景。幸運的是,我們生活在"future"世界,擁有一臺時間機器。接下來,我們一起坐上時光機,回到1997年吧。
if
語句
1997年1月1日,我們使用的是Python 1.4。現在有一個不同顏色組成的列表,我們想知道列表里每種顏色出現的次數。我們用字典來計算吧!
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
注意:我們沒有使用+=
,因為增量賦值直到Python 2.0才出現;另外,我們也沒有使用c in color_counts
這個慣用法(idiom),因為這也是Python 2.2中才發明的,
運行上述代碼之后,我們會發現color_counts
字典里,現在包含了列表中每種顏色的出現次數。
>>> color_counts
{'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1}
上面的實現很簡單。我們遍歷了每一種顏色,并判斷該顏色是否在字典中。如果不在,就在字典加入該顏色;如果在,就增加這種顏色的計數。
我們還可以把上面的代碼改寫為:
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
如果列表稀疏度高(即列表中不重復的顏色數量很多),這段代碼可能運行的會有點慢。因為我們現在要執行兩個語句,而不是一個。但是我們不關心性能問題,我們只關注編碼風格。經過思考,我們決定采用新版的代碼。
try
代碼塊(Code Block)
1997年1月2日,我們使用的還是Python 1.4。今早醒來的時候,我們突然意識到:我們的代碼遵循的是“三思而后行”(Look Before You Leap,即事先檢查每一種可能出現的情況)原則,但實際上我們應該按照“獲得諒解比獲得許可容易”(Easier to Ask Forgiveness, Than Permission,即不檢查,出了問題由異常處理來處理)的原則進行編程,因為后者更加簡潔、優雅。我們用try-except
代碼塊來重構下代碼吧:
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
現在,我們的代碼嘗試增加每種顏色的計數。如果某顏色不在字典里,那么就會拋出KeyError,我們隨之將該顏色的計數設置為1。
get
方法
1998年1月1日,我們已經升級到了Python 1.5。我們決定重構之前的代碼,使用字典中新增的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
現在,我們的代碼會遍歷每種顏色,從字典中獲取該顏色的當前計數值。如果沒有這個計數值,則該顏色的計數值默認為0,然后在數值的基礎上加1。***將字典中相應鍵的值設置為新的計數。
把主要代碼都寫在一行里,感覺很酷,但是我們不敢完全肯定這種做法更加簡潔、優雅。我們覺得可能有點太聰敏了,所以還是撤銷了這次的重構。
setdefault
方法
2001年1月1日,我們現在使用的是Python 2.0。我們聽說字典類型現在有一個setdefault
方法,決定利用它重構我們的代碼。我們還決定使用新增加的+=
增量賦值運算符。
colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = {} for c in colors: color_counts.setdefault(c, 0) color_counts[c] += 1
無論是否需要,我們在每一次循環時都會調用setdefault
方法。但這樣做,的確會讓代碼看上去可讀性更高。我們發現這種方法比之前的代碼更加簡潔、優雅,所以提交了此次修改。
fromkeys
方法
2004年1月1日,我們使用的是Python 2.3。我們聽說字典新增了一個叫fromkeys
的類方法(class method),可以利用列表中的元素作為鍵來構建字典。我們使用新方法重構了代碼:
colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = dict.fromkeys(colors, 0) for c in colors: color_counts[c] += 1
這段代碼將不同的顏色作為鍵,創建了一個新的字典,每個鍵的值被默認設置為0。這樣,我們增加每個鍵的值時,就不用擔心是否已經進行了設置。我們也不需要在代碼中進行檢查或異常處理了,這看上去是個改進。我們決定就這樣修改代碼。
推導式(Comprehension)與集合
2005年1月1日,我們現在用的是Python 2.4。我們發現可以利用集合(Python 2.3中發布,2.4版成為內置類型)與列表推導式(Python 2.0中發布)來解決計數問題。進一步思考之后,我想起來Python 2.4中還發布了生成器表達式(generator expressions),我們***決定不用列表推導式,而是采用生成器表達式。
colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = dict((c, colors.count(c)) for c in set(colors))
注意:我們這里使用的不是字典推導式,因為字典推導式直到Python 2.7才被發明。
運行成功了,而且只有一行代碼。但是這種代碼夠簡潔、優雅嗎 ?
我們想起了Python之禪(Zen of Python),這個Python編程指導原則起源于一個Python郵件列表,并悄悄地收進了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. # 優美勝過丑陋
Explicit is better than implicit. # 明確勝過含蓄
Simple is better than complex. # 簡單勝過復雜
Complex is better than complicated. # 復雜勝過難懂
Flat is better than nested. # 扁平勝過嵌套
Sparse is better than dense. # 稀疏勝過密集
Readability counts. # 易讀亦有價
Special cases aren't special enough to break the rules. # 特例也不能特殊到打破規則
Although practicality beats purity. # 盡管實用會擊敗純潔
Errors should never pass silently. # 錯誤永遠不應默默地溜掉
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. # 應該有一個–寧肯只有一個–明顯的實現方法
Although that way may not be obvious at first unless you're Dutch. # 也許這個方法開始不是很明顯,除非你是荷蘭人
Now is better than never. #現在做也要勝過不去做
Although never is often better than right now. # 盡管不做通常好過立刻做
If the implementation is hard to explain, it's a bad idea. # 如果實現很難解釋,那它就是一個壞想法
If the implementation is easy to explain, it may be a good idea. # 如果實現容易解釋,那它可能就是一個好想法
Namespaces are one honking great idea -- let's do more of those! # 命名空間是一個響亮的出色想法就讓我們多用用它們
我們的代碼變得更復雜,時間復雜度從O(n)增加到了O(n2);還變的更丑,可讀性更差了。我們那樣改,是一次有趣的嘗試,但是一行代碼的實現形式,沒有我們之前的方法簡潔、優雅。我們***還是決定撤銷修改。
defaultdict
方法
2007年1月1日,我們使用的是Python 2.5。我們剛發現,defaultdict
已經被加入標準庫。這樣,我們就可以把字典的默認鍵值設置為0了。讓我們使用defaultdict
重構代碼:
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
那個for
循環現在變得真簡單!這樣肯定是更加簡潔、優雅了。
我們發現,color_counts
這個變量的行為現在有點不同,但是它的確繼承了字典的特性,支持所有相同的映射功能。
>>> color_counts
defaultdict(<type 'int'>, {'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1})
我們在這里沒有把color_counts
轉換成字典,而是假設其他的代碼也使用鴨子類型(duck typing, Python中動態類型的一種,這里的意思是:其他代碼會將color_counts
視作字典類型),不再改動這個類似字典的對象。
Counter
類
2011年1月1日,我們使用的是Python 2.7。別人告訴我們,之前使用defaultdict
編寫的代碼,不再是統計顏色出現次數最簡潔、優雅的方法了。Python 2.7中新引入了一個Counter
類,可以完全解決我們的問題。
from collections import Counter
colors = ["brown", "red", "green", "yellow", "yellow", "brown", "brown", "black"] color_counts = Counter(colors)
還有比這更簡單的方法嗎?這個一定是最簡潔、優雅的實現了。
與defaultdict
一樣,Counter
類返回的也是一個類似字典的對象(實際是字典的一個子類)。這對滿足我們的需求來說足夠了,所以我們就這么干了。
>>> color_counts
Counter({'brown': 3, 'yellow': 2, 'green': 1, 'black': 1, 'red': 1})
性能考慮
請注意,在編寫這些實現方式時,我們都沒有關注效率問題。大部分方法的時間復雜度相同(O(n)),但是不同的Python語言實現形式(如CPython, PyPy,或者Jython)下,運行時間會有差異。
盡管性能不是我們的主要關注點,我還是在CPython 3.5.0的實現下測試了運行時間。從中,你會發現一個有趣的現象:隨著列表中顏色元素的密度(即相同元素的數量)變化,每一種實現方法的相對效率也會不同。
根據Python之禪,“ 應該有一個——寧肯只有一個明顯的實現方法”。這句話所說的狀態值得追求,但事實是,并不總是只有一種明顯的方法。這個“明顯”的方法會隨著時間、需求和專業水平,不斷地變化。
看完上述內容,你們掌握Python語言中計數方法的演變過程是怎么樣的的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。