美文网首页
Python 映射名称到序列元素

Python 映射名称到序列元素

作者: 大梦三千秋 | 来源:发表于2020-06-07 22:58 被阅读0次

    映射名称到序列元素


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

    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__

    以上为本篇的主要内容。

    相关文章

      网友评论

          本文标题:Python 映射名称到序列元素

          本文链接:https://www.haomeiwen.com/subject/wzxktktx.html