Python 映射名称到序列元素

映射名称到序列元素


一般访问列表或者元组的元素的时候,可能有限考虑的是通过下标索引进行访问,但是有些时候,这种情况会导致代码难以阅读。

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__

以上为本篇的主要内容。

发布了52 篇原创文章 · 获赞 16 · 访问量 5733

猜你喜欢

转载自blog.csdn.net/weixin_45642918/article/details/103652029
今日推荐