top of page
Search
Learn with Shin

資料結構小幫手 - collections



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


Post: Blog2_Post
bottom of page