Python 具名元組——我不只是可不變列表

封面.jpg

Intro

很多 Python 入門資料會將元組 (tuple) 介紹爲 “不可變列表”, 因其具備可迭代和可切片的能力, 同時無法修改元組中的值而得名. 然而這只是元組的其中一個特性而已.

元組的本質

元組是對數據的一個記錄, 每個位置記錄了某個字段的值, 位置和字段信息賦予了這組數據的意義. 聽起來很像數據庫中的某條數據記錄, 假如數據的元素不保持某個固定的位置, 你還認爲這個數據是可用的嗎?

具名元組 —— 元組特性的最有力體現

具名元組來自 Python 內置庫 collections.nametupled 中, 可以用來構建帶字段名的元組和一個相應的類

使用 nametupled 構建的類的實例所消耗的內存與元組是一致的, 因爲字段名都被保存在對應的類裏面.
—— 譯自 Fluent Python

聲明方式一

nametuple 構造函數的首個參數爲類名, 第二個參數爲字段名信息, 可以是以空格隔開的字符串, 也可以是字符串數組.

In [14]: from collections import namedtuple

In [15]: UserInfo = namedtuple('UserInfo', 'username passwor
    ...: d block vip')

In [16]: coding = UserInfo('coding', 'ShowMeTheCode', '0', '
    ...: 1')

In [17]: coding.password
Out[17]: 'ShowMeTheCode'

In [18]: coding[1]
Out[18]: 'ShowMeTheCode'

聲明方式二

這種方式利用了很 Pythonic 的解包方式進行聲明

In [19]: columns = ['name', 'city', 'email']

In [20]: Contact = namedtuple('Contact', columns)

In [21]: contact = ['coding', 'Dongguan', '[email protected]
    ...: m']

In [22]: coding_contact = Contact(*contact)

In [23]: coding_contact.city
Out[23]: 'Dongguan'

可以用元組進行解包聲明, 這裏就充分利用了元組的位置信息:

In [40]:  contact_desciption = ('Contact', ['name', 'city', 'email', 'phone'])

In [41]: Contact = namedtuple(*contact_desciption)

用途

  1. 面向對象
    在日常開發中, 往往離不開關係型數據庫對象和緩存, 以往使用 ORM 框架時, 受益於 ORM 面向對象的思想, 可以很方便的用 instance.field 方式訪問對象屬性, 但是轉化到緩存時, 特別是類似 Redis 這類只保存字節的緩存, 就失去了對象這一概念.
    以往我們常常使用字典來"挽回"一點面向對象的思想, 但是如前文引用所示, 不保存字段名的具名元組實例要比字典佔用的內存小, 並且在獲取對象屬性時要比字典方便多了, 面向對象的思想得到體現.
In [35]: coding_dict = {"name": "coding", "city": "Dongguan", "email": "fesonx@fox
    ...: mail.com"}

In [36]: coding_dict.get('name') # 字典
Out[36]: 'coding'

In [37]: coding_contact.name # 具名元組
Out[37]: 'coding'
  1. 轉化爲(類)字典對象
    日常開發中之所以會使用字典來保存緩存的內容, 很重要的原因是爲了方便解析爲 json 格式返回, 以往的 ORM 對象 (如 Flask-SQLALchemy) 如不引用外部框架, 一般也不具備轉爲鍵值的能力. 而具名字典正有這樣的特性, 利用內置的 _asdict() 方法即可:
In [20]: Contact = namedtuple('Contact', columns)

In [21]: contact = ['coding', 'Dongguan', '[email protected]
    ...: m']

In [22]: coding_contact = Contact(*contact)

In [26]: coding_contact._asdict()
Out[26]:
OrderedDict([('name', 'coding'),
             ('city', 'Dongguan'),
             ('email', '[email protected]')])

In [27]: import json

In [28]: json.dumps(coding_contact._asdict())
Out[28]: '{"name": "coding", "city": "Dongguan", "email": "[email protected]"}'

字典可以使用 .keys() 方法來得到鍵名列表, 而具名元組可以使用 ._fields 屬性獲得一個字段元組, 用來判斷前端傳入的排序屬性是否存在非常方便:

In [48]: Contact._fields
Out[48]: ('name', 'city', 'email', 'phone')

In [49]: 'city' in Contact._fields
Out[49]: True

歡迎關注公衆號: 程序員的碎碎念
個人博客: feson.tech

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章