映射名称到序列元素
一般访问列表或者元组的元素的时候,可能有限考虑的是通过下标索引进行访问,但是有些时候,这种情况会导致代码难以阅读。
namedtuple
Python 的 collections
模块中,namedtuple()
函数能够实现通过名称来访问元素。这个函数实际上一个返回 Python 中标准元组类型子类的一个工厂方法。
这里提及下 namedtuple()
需要传入的两个参数,第一个参数 typename
,是一个新的元组子类,这个子类主要是用于创建类元组的对象,可以通过域名来获取属性值,也可以通过索引和迭代获取值;第二个参数 field_names
,是一个像 ['x', 'y']
一样的字符串序列,另外 field_names
可以是一个纯字符串,用空白或都好分割开元素名,比如 x y
或者 x, y
。
示例代码如下:
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22) # 实例化的参数可以是位置参数,也可用关键字参数
>>> p # 以 name=value 格式列明内容
Point(x=11, y=22)
>>> p.x # 可以通过名称获取属性值
11
>>> p.y
22
虽然 namedtuple
实例看起来是类实例,但是它支持普通元组操作,例如索引和解压:
>>> p[0] + p[1] # 通过索引的形式获取值进行其他操作
33
>>> x, y = p # 像普通元组一样解压赋值
>>> x
11
>>> y
22
上面提及到第二个参数不一定是字符串序列,可以是用空格或者逗号分割元素,具体使用哪种形式根据个人喜好,下面用示例进行说明:
>>> p = namedtuple('Point', 'x, y') # 这里使用逗号分割元素
>>> p = Point(1,2)
>>> p
Point(x=1, y=2)
>>> p = namedtuple('Point', 'x y') # 这里使用空格分割元素
>>> p = Point(1,2)
>>> p
Point(x=1, y=2)
命名元组的主要用途是避免频繁的使用下标操作。这里主要避免的问题是,防止从数据库调用返回已有元组列表很大的情况下,表中添加新的列,代码很有可能会出错的情况。
下面用示例进行说明:
# 普通元组
def compute_cost(records):
total = 0.0
for rec in records:
total += rec[1] * rec[2]
return total
当看到这段代码的时候,其实没有直观的印象,下标操作究竟代指的是什么,而运算的目的是什么。下标操作会让表达的意思模糊,同时非常依赖数据的结构。下面是使用命名元组的例子:
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
这段代码中,能够直观的看出,想要计算的是持股的金额,用持股份额乘上持股价格。
_replace()
命名元组还有一个用途是用于作为字典的替代,因为字典往往要更多的内存空间,相比较之下,用命名元组更加高效。但是,这里有个限制,命名元组并不能像字典一样可以进行修改。示例如下:
>>> s = Stock('AAPL', 100, 279.44)
>>> s
Stock(name='AAPL', shares=100, price=279.44)
>>> s.shares = 200
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
如果真的有需要改变属性的值,命名元组示例有个 _replace()
方法,它能够创建全新的命名元组并将对应的字段用新的值取代,示例如下:
>>> id(s) # 先获取未改变的地址
43683416
>>> s = s._replace(shares=75)
>>> id(s) # 改变后的地址,可以发现两者是不同的
43682296
>>> s
Stock(name='AAPL', shares=75, price=279.44)
_replace()
方法还有一个特性,是当命名元组拥有可选或者缺失字段的时候,能够作为填充数据的方法。具体的用法是先创建包含缺省值的原型元组,然后使用 _replace()
方法创建新的值更新实例。实例如下:
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
# 创建一个原型元组实例
stock_prototype = Stock('', 0, 0.0, None, None)
# 将普通字典转换为 Stock 元组类
def dict_to_stock(s):
return stock_prototype._replace(**s)
下面是代码的使用方法:
>>> a = {'name': 'AAPL', 'shares': 100, 'price': 279.44}
>>> dict_to_stock(a)
Stock(name='AAPL', shares=100, price=279.44, date=None, time=None)
>>> b = {'name': 'AAPL', 'shares': 100, 'price': 279.44, 'date': '12/20/2019'}
>>> dict_to_stock(b)
Stock(name='AAPL', shares=100, price=279.44, date='12/20/2019', time=None)
上面就是 _replace
的一些用法,但是如果需求是定义需要频繁实例属性的高效数据结果,命名元组并不是最佳的选择,这个时候应该考虑定义一个含 __slots__
方法的类。这个方法后面会提及,也可以直接访问官方文档进行了解,具体链接:__slots__。
以上为本篇的主要内容。
网友评论