問題
通過下標(biāo)訪問列表或元組中的元素,代碼可讀性不強(qiáng),可以通過名稱來訪問元素。
解決方案
collections.namedtuple() 函數(shù)是繼承自tuple的子類??梢詣?chuàng)建自定義元素個(gè)數(shù)的tuple對(duì)象,它具備tuple的不變性,又可以根據(jù)屬性而不是索引來引用其中的元素。代碼示例:
from collections import namedtuple
User = namedtuple('User', ['mail_add', 'joined_date'])
print(User)
<class '__main__.User'>
user = User('python@gmail.com', '2017-11-10')
print(user)
print(user.mail_add)
print(user.joined_date)
User(mail_add='python@gmail.com', joined_date='2017-11-10')
python@gmail.com
2017-11-10
盡管 namedtuple 的實(shí)例看起來像一個(gè)普通的類實(shí)例,但是它跟元組類型是可交換的,支持所有的普通元組操作,如索引和解壓。 比如:
print(len(user))
mail, joined = user
print(mail)
print(joined)
2
python@gmail.com
2017-11-10
命名元組的主要用途是將代碼從下標(biāo)操作中解脫出來。 因此,如果在數(shù)據(jù)庫調(diào)用中返回了一個(gè)很大的元組列表,通過下標(biāo)去操作其中的元素, 當(dāng)數(shù)據(jù)庫表中添加了新的列時(shí),代碼可能就會(huì)出錯(cuò),但如果使用了命名元組,就不會(huì)有這樣的顧慮。
為了說明清楚,下面是使用普通元組的代碼:
def cumputer_cost(records):
total = 0.0
for record in records:
total += record[1] * record[2]
return total
下標(biāo)操作通常會(huì)讓代碼表意不清晰,并且非常依賴記錄的結(jié)構(gòu)。 下面是使用命名元組的版本:
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price'])
def cumputer_cost(records):
total = 0.0
for record in records:
r = Stock(*record)
total += s.shares * s.price
return total
討論
命名元組另一個(gè)用途是作為字典的替代,因?yàn)樽值浯鎯?chǔ)需要更多的內(nèi)存空間。 如果需要構(gòu)建一個(gè)非常大的包含字典的數(shù)據(jù)結(jié)構(gòu),那么使用命名元組會(huì)更加高效。 但是需要注意的是,不像字典那樣,一個(gè)命名元組是不可更改的。比如:
s = Stock('ACME', 100, 123.45)
s.shares = 75
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
s.shares = 75
AttributeError: can't set attribute
如果確實(shí)需要改變屬性的值,可以使用命名元組實(shí)例的 _replace() 方法, 它會(huì)創(chuàng)建一個(gè)全新的命名元組,并將對(duì)應(yīng)的字段用新的值取代。比如:
s = Stock('ACME', 100, 123.45)
s = s._replace(shares=75)
print(s)
Stock(name='ACME', shares=75, price=123.45)
最后要說的是,如果目標(biāo)是定義一個(gè)需要更新很多實(shí)例屬性的高效數(shù)據(jù)結(jié)構(gòu),那么命名元組并不是最佳選擇。 這時(shí)候應(yīng)該考慮定義一個(gè)包含 slots 方法的類。