映射名字到序列元素
通常列表或元组通过位置(索引)来访问元素,但这使得代码有时难以阅读。 通常我们希望通过按名称访问元素来减少对位置的依赖。
collections模块的namedtuple()函数(实际上是一个工厂方法)提供了一个tuple的子类,该类拥有tuple的常规操作,同时提供了按名称访问序列元素的能力。
命名元组为元组中的每个位置赋予含义,并允许更可读,自我记录的代码。 它们可以在使用常规元组的任何地方使用,并且它们添加了按名称而不是位置索引访问字段的功能。
如下示例:
>>> from collections import namedtuple
>>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
>>> sub = Subscriber('jonesy@example.com', '2012-10-19')
>>> sub
Subscriber(addr='jonesy@example.com', joined='2012-10-19')
>>> sub.addr
jonesy@example.com
>>> sub.joined
'2012-10-19'
>>>
尽管namedtuple的实例看起来像普通的类实例,但它可以与元组交换,并支持所有常用的元组操作,例如索引和解包。 例如:
>>> len(sub)
2
>>> addr, joined = sub
>>> addr
'jonesy@example.com'
>>> joined
'2012-10-19'
>>>
命名元组的一个主要用途是将代码与其操作的元素的位置分离。 因此,如果从数据库调用中获取大量元组,然后通过访问位置元素来操作它们,那么,如果向表中添加了新列,则代码可能会中断。 如果首先将返回的元组强制转换为namedtuples,则不是这样。 下面用一个代码片段来做解释: ```Python def compute_cost(records): total = 0.0 for rec in records: total += rec[1] * rec[2] return total ``` 以上代码可以看出,计算股票的价格以来元组中位置为1和2的元素,若在某个时间节点在数据库中1和2之间插入了一列,则整个代码将无法使用。 以下是通过namedtuple()的改写: ```Python from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price'])
def compute_cost(records):
total = 0.0
for rec in records:
s = Stock(*rec)
total += s.shares * s.price
return total
namedtuple的一种可能用途是替换字典,因为字典需要更多的空间来存储。 因此,如果要构建涉及字典的大型数据结构,则使用namedtuple会更有效。 但是,请注意,与字典不同,namedtuple是不可变(immutable)的。
```Python
>>> s = Stock('ACME', 100, 123.45)
>>> s
Stock(name='ACME', shares=100, price=123.45)
>>> s.shares = 75
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>>
如果要更改namedtuple的任意属性,可以使用该类实例的_replace()方法。
>>> s = s._replace(shares=75)
>>> s
Stock(name='ACME', shares=75, price=123.45)
>>>
实际上该方法并没有更新原有类实例,而是创建了一个全新的对象。
_replace()方法的一个巧妙用途是它可以用来填充具有可选或缺少字段的命名元组。 为此,创建一个包含默认值的原型元组,然后使用_replace()创建替换值的新实例。 例如:
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
# Create a prototype instance
stock_prototype = Stock('', 0, 0.0, None, None)
# Function to convert a dictionary to a Stock
def dict_to_stock(s):
return stock_prototype._replace(**s)
>>> a = {'name': 'ACME', 'shares': 100, 'price': 123.45}
>>> dict_to_stock(a)
Stock(name='ACME', shares=100, price=123.45, date=None, time=None)
>>> b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}
>>> dict_to_stock(b)
Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
>>>