在看pytest測試實戰一書中,看到了python namedtuple這個模塊,在網上查詢了一下還挺好用的,記錄下來:
摘自https://www.jianshu.com/p/60e6484a7088
首先,我會介紹下使用namedtuple
所需要了解的基本概念,然后講解如何使用namedtuple
,最后使用namedtuple
來創建一摞紙牌。理解這些之后,就可以權衡利弊,並在生產中使用
基本概念
namedtuple
是一個 工廠函數,定義在python
標准庫的collections
模塊中,使用此函數可以創建一個可讀性更強的元組namedtuple
函數所創建(返回)的是一個 元組的子類(python中基本數據類型都是類,且可以在buildins
模塊中找到)namedtuple
函數所創建元組,中文名稱為 具名元組- 在使用普通元組的時候,我們只能通過
index
來訪問元組中的某個數據 - 使用具名元組,我們既可以使用
index
來訪問,也可以使用具名元組中每個字段的名稱來訪問 - 值得注意的是,具名元組和普通元組所需要的內存空間相同,所以 不必使用性能來權衡是否使用具名元組
如何使用
namedtuple
是一個函數,我們先來看下他的參數
參數解析
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None):
有兩個必填參數typename
和field_names
typename
- 參數類型為字符串
- 具名元組返回一個元組子對象,我們要為這個對象命名,傳入
typename
參數即可
field_names
- 參數類型為字符串序列
- 用於為創建的元組的每個元素命名,可以傳入像
['a', 'b']
這樣的序列,也可以傳入'a b'
或'a, b'
這種被逗號或空格分割的單字符串 - 必須是合法的標識符。不能是關鍵字如
class,def
等
rename
- 注意的參數中使用了
*
,其后的所有參數必須指定關鍵字 - 參數為布爾值
- 默認為
False
。當我們指定為True
時,如果定義field_names
參數時,出現非法參數時,會將其替換為位置名稱。如['abc', 'def', 'ghi', 'abc']
會被替換為['abc', '_1', 'ghi', '_3']
defaults
- 參數為
None
或者可迭代對象 - 當此參數為
None
時,創建具名元組的實例時,必須要根據field_names
傳遞指定數量的參數 - 當設置
defaults
時,我們就為具名元組的元素賦予了默認值,被賦予默認值的元素在實例化的時候可以不傳入 - 當
defaults
傳入的序列長度和field_names
不一致時,函數默認會右側優先 - 如果
field_names
是['x', 'y', 'z']
,defaults
是(1, 2)
,那么x
是實例化必填參數,y
默認為1
,z
默認為2
基本使用
理解了namedtuple
函數的參數,我們就可以創建具名元組了
>>> Point = namedtuple('Point', ['x', 'y']) # 返回一個名為`Point`的類,並賦值給名為`Point`的變量 >>> p = Point(11, y=22) # 可以根據參數的位置,或具名參數來實例化(像普通的類一樣) >>> p[0] + p[1] # 具名元組可以像普通元組一樣通過`index`訪問 33 >>> x, y = p # 具名元組可以像普通元組一樣解包 >>> x, y (11, 22) >>> p.x + p.y # 具名元組還可以通過屬性名稱訪問元組內容 33 >>> p # 具名元組在調用`__repr__`,打印實例時,更具可讀性 Point(x=11, y=22)
具名元組在存儲csv
或者sqlite3
返回數據的時候特別有用
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') import csv for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): print(emp.name, emp.title) import sqlite3 conn = sqlite3.connect('/companydata') cursor = conn.cursor() cursor.execute('SELECT name, age, title, department, paygrade FROM employees') for emp in map(EmployeeRecord._make, cursor.fetchall()): print(emp.name, emp.title)
特性
具名元組除了擁有繼承自基本元組的所有方法之外,還提供了額外的三個方法和兩個屬性,為了防止命名沖突,這些方法都會以下划線開頭
_make(iterable)
這是一個類函數,參數是一個迭代器,可以使用這個函數來構建具名元組實例
>>> t = [11, 22] >>> Point._make(t) Point(x=11, y=22)
_asdict()
實例方法,根據具名元組的名稱和其元素值,構建一個OrderedDict
返回
>>> p = Point(x=11, y=22) >>> p._asdict() OrderedDict([('x', 11), ('y', 22)])
_replace(**kwargs)
實例方法,根據傳入的關鍵詞參數,替換具名元組的相關參數,然后返回一個新的具名元組
>>> p = Point(x=11, y=22) >>> p._replace(x=33) Point(x=33, y=22) >>> for partnum, record in inventory.items(): ... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
_fields
這是一個實例屬性,存儲了此具名元組的元素名稱元組,在根據已經存在的具名元組創建新的具名元組的時候使用
>>> p._fields # view the field names ('x', 'y') >>> Color = namedtuple('Color', 'red green blue') >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields) >>> Pixel(11, 22, 128, 255, 0) Pixel(x=11, y=22, red=128, green=255, blue=0)
_fields_defaults
查看具名元組類的默認值
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0]) >>> Account._fields_defaults {'balance': 0} >>> Account('premium') Account(type='premium', balance=0)
使用技巧
- 使用
getattr
獲取具名元組元素值
>>> getattr(p, 'x') 11
- 將字典轉換為具名元組
>>> d = {'x': 11, 'y': 22} >>> Point(**d) Point(x=11, y=22)
- 既然具名元組是一個類,我們當然可以隨心所欲的進行定制
>>> class Point(namedtuple('Point', ['x', 'y'])): ... __slots__ = () ... @property ... def hypot(self): ... return (self.x ** 2 + self.y ** 2) ** 0.5 ... def __str__(self): ... return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) >>> for p in Point(3, 4), Point(14, 5/7): ... print(p) Point: x= 3.000 y= 4.000 hypot= 5.000 Point: x=14.000 y= 0.714 hypot=14.018
__slots__
值的設置可以保證具名元組保持最小的內存占用
namedtuple紙牌
import collections
# 將紙牌定義為具名元組,每個紙牌都有等級和花色 Card = collections.namedtuple('Card', 'rank suit') class FrenchDeck: # 等級2-A ranks = [str(n) for n in range(2,11)] + list('JQKA') # 花色紅黑方草 suits = 'spades diamonds clubs hearts'.split() # 構建紙牌 def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] # 獲取紙牌 def __getitem__(self, position): return self._cards[position] >>> french_deck = FrenchDeck() >>> french_deck[0] Card(rank='2', suit='spades') >>> french_deck[0].rank '2' >>> french_deck[0].suit 'spades'
作者:天邊一鈎殘月帶三星
鏈接:https://www.jianshu.com/p/60e6484a7088
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。