有屬性名稱的列表:Python 的 NamedTuple 介紹

Makzan
·
·
IPFS
·
NamedTuple 如其名,也是 Tuple 的一種,所以 Tuple 有的特性,也可以應用於 NamedTuple,例如按 [index] 或 [start:end] 來存取。或 unpack 等功能,例子如下。之前介紹過的 ISOCalendar,也在 Python 3.9 中從原有的 Tuple 升級為 NamedTuple,直接印出可以看到 Tuple 但每個屬性皆有名稱。

Python 中,常常我們需要用 Dict 字典來儲存數據。只要每筆記錄有不同的屬性,Dict 類型就是自然之選。但 Dict 的彈性同時帶來缺點,就是如果儲存時用錯索引(Key),就會在取得時出現錯誤。

sample_contact = {    
    "name": "Tom",
    "email": "tom@example.com",
    "birth_month": 10
}

print(sample_contact["name"])                 # Result: Tom
print(sample_contact.get("name", "Untitled")) # Result: Tom

當然,我們也可以通過 Dict 的 get 函數來確保取出值時,就算 Key 不慎不存在,也不至於報錯彈 App。

sample_contact = {
    "名稱": "Tom",
    "email": "tom@example.com",
    "birth_month": 10
}

print(sample_contact.get("name", "Untitled")) # Result: Untitled
print(sample_contact["name"]) # Result: 錯誤

這解決了報錯問題,但沒有解決資訊入錯 Key 而存取不了的問題。此時,可以改用 NamedTuple 類型來解決。

NamedTuple 介紹

NamedTuple 首先需要定義一個範本,即這筆記錄有甚麼屬性,需要預先製定。例如一個 CRM 系統,假設每個客人記錄有名稱(name)、電郵(email)、生日月份(birth_month)等。我們就可以使用以下代碼定義這個客戶記錄。

from collections import namedtuple

Contact = namedtuple("Contact", "name, email, birth_month")

以上是範本,但還未有由此範本而生成的記錄。

定義範本,並生成實體

生成記錄

然後,當我們需要一筆新記錄。則可以使用:

sample_contact = Contact("Tom", "tom@example.com", birth_month=10)

這樣,我們便從 Customer 範本生成了一個實體。

注意我們的範本的命名使用了大寫字母開頭,而每個實體使用細寫開頭。

若果我們將此 customer 列印出來,會見 Python 將當中每個屬性的名稱也列出。

print(sample_contact) 
# Result: Contact(name='Tom', email='tom@example.com', birth_month=10)

Tuple 特性與 ISO Calendar 例子

NamedTuple 如其名,也是 Tuple 的一種,所以 Tuple 有的特性,也可以應用於 NamedTuple,例如按 [index] 或 [start:end] 來存取。或 unpack 等功能,例子如下。

之前介紹過的 ISO Calendar,也在 Python 3.9 中從原有的 Tuple 升級為 NamedTuple,直接印出可以看到 Tuple 但每個屬性皆有名稱。

以下範例代碼,得出今日日期的 ISO Calendar 值後,分別印出來、檢視類型、按屬性名稱存取、按 [index] 取值等。並使用以往慣常於 ISO Calendar 使用的 unpack方法也可,例如這個例子就把 ISOCalendar 的三個值分別裝到 year, week, weekday 三個變數。

import datetime

today_iso_calendar = datetime.date.today().isocalendar()

print(today_iso_calendar)
print("type", type(today_iso_calendar))

print("week", today_iso_calendar.week)
print("year", today_iso_calendar[0])

year, week, weekday = today_iso_calendar

print(f"{year}-W{week}-{weekday}")

運行結果:

datetime.IsoCalendarDate(year=2022, week=4, weekday=3)
type <class 'datetime.IsoCalendarDate'>
week 4
year 2022
2022-W4-3

所以 NamedTuple 是介乎於 Tuple 唯讀列表與 Dict 字典的類型。

運行結果

減低錯誤機會

而取得這個客戶的屬性時,由於直接使用 .name 等來存取,而不是以字串作為索引。這也可以確保我們使用正確的屬性,因為如果我們粗心打錯字,代碼編輯器便會馬上報錯,而不是等到運行時才出現錯誤。

sample_contact = Contact("Tom", "tom@example.com", birth_month=10)
print(sample_contact.name) # Result: Tom

這樣便能減低程式在我們交付於他人使用時才出現此等運行錯誤的可能性。亦使程式更穩定,亦使程式打印出來時更清晰。


— 麥麥寫的 麥誠 Makzan,2022-01-26。

CC BY-NC-ND 2.0

Like my work? Don't forget to support and clap, let me know that you are with me on the road of creation. Keep this enthusiasm together!

logbook icon
Makzan我管理世界職業技能競賽之網站技術項目、舉辦本地設計與開發賽事、開課分享技術心得。一個用網頁來表達自己的作家。
  • Author
  • More

期望與放下

甚麼是世界職業技能競賽

【最後兩天】買《所謂「我不投資」,就是 all in 在法定貨幣》親身體驗擁有 NFT