Python被稱作 "battery included" 的程式語言。不是它真的有附電池🔋,而是指Python本身就具備相當多的內建模組來幫助我們更容易的寫程式。
舉個例來說,就像是某車款你買基本型號的車種,本身標配就有倒車雷達、自動偵測煞車等等,相當不錯。你當然可以追加Premium或是外掛的配備,但是我們應該先了解基本配備是不是已經符合我們的需求,是吧?
因此,今天想要來跟大家介紹一個常用的Python內建模組: collections
import collections
collections主要針對容器型(container)的資料結構(像是list, tuple, dictionary這一類的型態)提供一些輔助的選項,幫助我們解決一些常遇到的問題。
今天我們就來看其中一部分我個人覺得相當好用的功能吧 ~🌞
Counter
請問這一個籃子裡有幾個蘋果,幾根香蕉,幾個芒果?或者說有幾種水果,每個水果各有幾個?
這種單純數數量的作業,不是只有小朋友的作業會出現,其實我們寫程式中也經常會需要做這件事。
譬如說我們有一個List長的像這樣:
fruit_bucket = ["banana", "apple", "apple", "mango", "banana", "apple"]
當然你可以自己寫一套方法去數每一水果出現幾次(例如說這裡)。
不過,我們可以利用collections中一個叫做Counter的資料類型,幫我們快速的完成這項任務喔!
不多說,直接就來看Code吧:
像這樣,透過Counter我們可以馬上整理出每個元素的出現次數。很不錯吧?
Counter的物件就像是一般的dictionary,具有像.items,.update等功能。除此之外還具有一些方便的method供我們使用。
譬如說,我可以直接拿到出現次數最多的元素:
利用.most_common(2) ,我們可以拿到兩個出現次數最多元素。如果你只要最多的那一個的話,就用 .most_common(1) ,以此類推。
Counter同時具有類似dictionary的特性,如果你想知道 apple 出現過幾次,只要把apple當作Key:
是不是省了我們不少時間?人生沒有時間讓你一個一個慢慢數啊~😐
其他Counter可以被應用的地方像是,在一篇文章中找出最常出現的字。像我們經常看到的文字雲(Word Cloud)就是一個例子。
defaultdict
當我們使用Python的dictionary時,如果指定的Key不存在的話,就會得到一個Key Error ...🤬
相信大家都有經驗吧?
collections裡面有一個專門解決這個問題的方法!那就是 defaultdict!
defaultdict如其名,它是一個dictionary,但是可以讓你預先設定想要的數值(default)。如果你要的Key不存在的話,defaultdict就會自動幫你做一個Key,並賦予預設的數值。代表你可以跟討厭的Key Error說掰掰囉~
他的目的就是這麼單純 😇。
但是,使用上對初學者可能沒那麼直覺...所以我們就來看看例子吧:
使用defaultdict的時候,需要給它的argument必須是一個function,或稱之為callable,意思是可以被call的物件。(...如果你不給它任何argument,它就只會像一般的dictionary,無法產生預設值並會產生KeyError喔~)
上面的例子我們看到的int其實是一個python內建的function喔。當你call它的時候 (不給任何的argument),你會得到的是數字的0。
再看一個例子吧。list 本身也是一個內建的funciton,如果你單純去call它的話,會得到的是一個空的list:
回到defaultdict,當指定的Key不存在的時候,defaultdict就會去call你給它的function,並將回傳的資料作為預設值。
所以其實我們也像這樣可以給它一個自製的function喔:
這樣應該比較容易理解了吧 😃。
這裡我們來看一個例子。假設我們有一組資料關於每個學生的所屬班級:
class_data = [
("CLASS_A", "Bob"),
("CLASS_B", "John"),
("CLASS_B", "Alex"),
("CLASS_A", "Ken"),
("CLASS_A", "Kevin"),
("CLASS_C", "Mary"),
]
我想要將這筆資料轉換成像👇這樣,可以用班級找到該班的學生:
{"CLASS_A": ["Bob", "Ken", "Kevin"], "CLASS_B":... }
可以怎麼做呢?...我們就用defaultdict來試試看吧:
是不是超簡潔!
利用defaultdict,我們節省了兩個步驟喔:
1. 確認Key是否存在 (為了防止KeyError)
2. 建立一個空的list給新的Key (為了能做append這個動作)
善用defaultdict會讓我們的Code更加的簡潔,而且因為不用自己去寫這些邏輯(Reinventing the wheel)讓我們盡量避開討厭的蟲蟲危機(bugs)喔~🐞
namedtuple
Python的tuple和list一樣,都是透過index來擷取裡面的元素。
如果你今天有一個tuple長這樣:
row = ("John", 30, "A")
假設你已經事先知道第一個element代表名字,第二個代表年紀,第三個代表血型,如果今天你想要拿到血型,你可以用index 的數字 2 來抓到它(index 從0開始)。
row[2]
但是說實在話,這樣子用index來表示你要的元素,對於讀code的人來說其實是蠻不友善的。row[2]代表的是什麼,只有寫的人才知道(或是自己都忘記了也有可能)。
因此,理想上來說,如果code本身能夠用具意義的名字來標示這些元素,就能夠讓Code更容易懂。
namedtuple這個功能可以幫你做出,顧名思義,帶有名字的tuple。
使用方法如下:
namedtuple的第一個argument是你給它的一個type的名字,在例子中我們就叫它 "Person"。這個相當於我們做一個叫做Person的class。
第二個argument是你想要它具備的fields。我們可以給他一個list,裡面含有我們要的fields的名字。在例子中我們指定了名字,年齡,跟血型( ["name", "age", "blood_type"])。這個相當於class中的attributes。
基本上namedtuple的功能就像是幫你快速的做出一個迷你class😉。利用它生成出來的物件,上述的例子你可以用像p.age來拿到你要的年紀的數據。但是同時它還是保留tuple的特性:可以讓你用index去擷取數據。而且你不可以去改它的內容(immutable)。
承接上面的例子,譬如說,你有一個csv的文件長的像這樣:
John, 30, A
Ken, 28, A
Bob, 35, B
Alex, 40, A
你可以利用namedtuple來針對每一個field命名:
如第十行所示,我們可以避免用index(像是row[0]),取而代之用名字(user.name)來代表你要的內容。
上面的User._make可以接受像是list, tuple或是dictionary等資料類型來生成物件。
如果覺得用_make好像不夠正規的話,也可以用像:
user = User(*row)
的方式來生成物件喔。
小結
今天一次介紹了三個Python的collections模組的實用工具:
Counter
defaultdict
namedtuple
當然collections這個模組還有其他的資料結構工具,有興趣可參考官方網頁。
雖然有些概念或用法可能剛開始沒那麼直覺,也需要一些練習來理解,但是善用這些小工具可以幫助我們寫出更好讀,好維持的Code喔 👍🏻~
Comments