namedtuple工廠函式精講
首先,我會介紹下使用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)
具名元組在儲存ofollow,noindex">
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.3fy=%6.3fhypot=%6.3f' % (self.x, self.y, self.hypot) >>> for p in Point(3, 4), Point(14, 5/7): ...print(p) Point: x= 3.000y= 4.000hypot= 5.000 Point: x=14.000y= 0.714hypot=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'