一道題讓你從此真正理解Python編程
↑?關(guān)注 + 星標(biāo)?,每天學(xué)Python新技能
后臺回復(fù)【大禮包】送你Python自學(xué)大禮包
寫下這個題目的時候,腦海里無法抑制地響起了周華健那略帶沙啞的歌聲:
遠(yuǎn)處傳來那首熟悉的歌,
那些心聲為何那樣微弱。
很久不見,你現(xiàn)在都還好嗎?
有沒有那么一首歌,
會讓你輕輕跟著和,
隨著我們生命起伏,
一起唱的主題歌;
有沒有那么一首歌,
會讓你突然想起我,
讓你歡喜也讓你憂,
這么一個我……
這道題,名為《列表中的幸運數(shù)》。什么是幸運數(shù)呢?在整數(shù)列表中,如果一個數(shù)字的出現(xiàn)頻次和它的數(shù)值大小相等,我們就稱這個數(shù)字為「幸運數(shù)」。例如,在列表[1, 2, 2, 3]中,數(shù)字1和數(shù)字2出現(xiàn)的次數(shù)分別是1和2,所以它們是幸運數(shù),但3只出現(xiàn)過1次,3不是幸運數(shù)。
明白了幸運數(shù)的概念,我們就來試著找出列表[3, 5, 2, 7, 3, 1, 2 ,4, 8, 9, 3]中的幸運數(shù)吧。這個過程可以分為以下幾個步驟:
找出列表中不重復(fù)的數(shù)字
統(tǒng)計每個數(shù)字在列表中出現(xiàn)的次數(shù)
找出出現(xiàn)次數(shù)等于數(shù)字本身的那些數(shù)字
第1步,找出列表中不重復(fù)的數(shù)字
找出列表中不重復(fù)的數(shù)字,也就是去除列表中的重復(fù)元素,簡稱“去重”。去重最簡潔的方法是使用集合。
>>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>>> unique = set(arr)>>> unique{1,?2,?3,?4,?5,?7,?8,?9}
第2步,統(tǒng)計每個數(shù)字在列表中出現(xiàn)的次數(shù)
我們知道,列表對象自帶一個count()方法,能返回某個元素在列表中出現(xiàn)的次數(shù),具體用法如下:
>>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>>> arr.count(8) # 元素8在數(shù)組arr中出現(xiàn)過2次2
接下來,我們只需要遍歷去重后的各個元素,逐一統(tǒng)計它們各自出現(xiàn)的次數(shù),并保存成一個合適的數(shù)據(jù)結(jié)構(gòu),這一步工作就萬事大吉了。
>>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>>> unique = set(arr) # 去除重復(fù)元素>>> pairs = list() # 空列表,用于保存數(shù)組元素和出現(xiàn)次數(shù)組成的元組>>> for i in unique:pairs.append((i, arr.count(i)))>>> pairs[(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)]
作為新手,代碼寫成這樣,已經(jīng)很不錯了。但是,一個有追求的程序員絕對不會就此自滿、裹足不前。他們最喜歡做的事情就是想盡千方百計消滅for循環(huán),比如使用映射函數(shù)、過濾函數(shù)取代for循環(huán);即便不能拒絕for循環(huán),他們也會盡可能把循環(huán)藏起來,比如藏在列表推導(dǎo)式內(nèi)。這里既然是要對每一個元素都調(diào)用列表的count()這個方法,那就最適合用map函數(shù)取代for循環(huán)了。
>>>?m?=?map(arr.count,?unique)>>> m>>> list(m) # 生成器可以轉(zhuǎn)成列表[1, 2, 3, 1, 1, 1, 2, 1]>>> list(m) # 生成器只能用一次,用過之后,就自動清理了[]
map函數(shù)返回的是一個生成器(generator),可以像列表一樣遍歷,但無法像列表那樣直觀地看到各個元素,除非我們用list()把這個生成器轉(zhuǎn)成列表(實際上并不需要將生成器轉(zhuǎn)為列表)。請注意,生成器和迭代器不同,或者說生成器是一種特殊的迭代器,只能被遍歷一次,遍歷結(jié)束,就自動消失了。迭代器則可以反復(fù)遍歷。比如,range()函數(shù)返回的就是迭代器:
>>>?a?=?range(5)>>> list(a)[0, 1, 2, 3, 4]>>> list(a)[0, 1, 2, 3, 4]
說完生成器和迭代器,咱們還得回到原來的話題上。使用map映射函數(shù),我們得到了每個元素的出現(xiàn)次數(shù),還需要和對應(yīng)的元素組成一個一個的元組。這時候,就用上zip()函數(shù)了。zip() 函數(shù)創(chuàng)建一個生成器,用來聚合每個可迭代對象(迭代器、生成器、列表、元組、集合、字符串等)的元素,元素按照相同下標(biāo)聚合,長度不同則忽略大于最短迭代對象長度的元素。
>>>?m?=?map(arr.count,?unique)>>> z = zip(unique, m)>>> z>>> list(z)[(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)]>>> list(z)[]
很顯然,zip()函數(shù)返回的也是生成器,只能用一次,過后即消失。
第3步,找出出現(xiàn)次數(shù)等于數(shù)字本身的那些數(shù)字
有了每個元素及其出現(xiàn)的次數(shù),我們只需要循環(huán)遍歷……不,請稍等,我們?yōu)槭裁匆欢ㄒh(huán)呢?我們只是要把每個元素過濾一遍,找出那些出現(xiàn)次數(shù)等于元素自身的那些元組,為什么不試試過濾函數(shù)filter()呢?
>>>?def?func(x):?#?參數(shù)x是元組類型if x[0] == x[1]:return x>>> m = map(arr.count, unique)>>> z = zip(unique, m)>>> f = filter(func, z)>>> f>>> list(f)[(1, 1), (2, 2), (3, 3)]>>> list(f)[]
過濾函數(shù)filter()接受兩個參數(shù),第1個參數(shù)是個函數(shù),用于判斷一個元素是否符合過濾條件,第2個參數(shù)就是需要過濾的可迭代對象了。filter()函數(shù)返回的也是生成器,只能用一次,過后即消失。
寫這里,我們幾乎要大功告成了。但是,作為一個有追求的程序員,你能容忍func()這樣一個看起來怪怪的函數(shù)嗎?答案是不能!你一定會用lambda函數(shù)取代它。另外,也許我們還需要對結(jié)果按照元素的大小排序。加上排序,完整代碼如下:
>>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>>> unique = set(arr)>>> m = map(arr.count, unique)>>> z = zip(unique, m)>>> f = filter(lambda x:x[0]==x[1], z)>>> s = sorted(f, key=lambda x:x[0])>>> print('幸運數(shù)是:', [item[0] for item in s])幸運數(shù)是:[1, 2, 3]
終極代碼,一行搞定
如果你曾經(jīng)有過被那些寫成一行、卻能實現(xiàn)復(fù)雜功能的、看起來像天書一樣的代碼蹂躪的痛苦經(jīng)歷,那么,現(xiàn)在你也可以把上面的代碼寫成一行,去蹂躪別人了。
>>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>>> print('幸運數(shù)是:', [item[0] for item in sorted(filter(lambda x:x[0]==x[1], zip(set(arr), map(arr.count, set(arr)))), key=lambda x:x[0])])幸運數(shù)是:[1, 2, 3]
戲劇性反轉(zhuǎn),這次真的理解Python了!
有人說,何必那么麻煩呢?這樣寫不是更簡單、更易讀嗎?果然,我真是想多了!
>>?arr?=?[3,5,2,7,3,8,1,2,4,8,9,3]>> [x for x in set(arr) if x == arr.count(x)][1,?2,?3]
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。本文鏈接:https://blog.csdn.net/xufive/article/details/105215593
推薦閱讀
