> 文章列表 > Python namedtuple:构建更清晰、更易于维护的代码

Python namedtuple:构建更清晰、更易于维护的代码

Python namedtuple:构建更清晰、更易于维护的代码

文章目录

  • 参考
  • 描述
  • 具名元组
      • 优点
      • 性能
      • 元素的命名
          • ValueError
          • 私有属性或方法
      • 小试牛刀
  • namedtuple
      • 参数
      • rename
      • defaults
  • namedtuple 对象中的常用属性及方法
      • 总览
      • _fields
      • _field_defaults
      • _make(iterable)
      • _asdict()
      • _replace(**kwargs)

参考

项目 描述
Python 标准库 DougHellmann / 刘炽 等
搜索引擎 Bing
Python 官方文档 collections — 容器数据类型

描述

项目 描述
Python 解释器 3.10.6

具名元组

优点

使用 Python 标准库 collections 中的 namedtuple 类你将能够创建一个具名元组,具名元组相比于元组具有以下优点:

  1. 更丰富的特性
    具名元组继承自 Python 内置的元组,在此基础上还添加了许多有用的特性,如字段名、_asdict() 方法、默认值等。这些特性让具名元组更加灵活,能够满足更多的场景需求。

  2. 更优秀的可维护性与可读性
    具名元组中的元素是具有名称,这意味着你可以使用名称而不是索引来访问目标元素。在定义具名元组的过程中,使用更合适的词语作为元素名称将能够有效提高代码的可维护性与可读性。

性能

在大多数情况下,namedtuple 的性能与 Python 内置的元组类型相当。 这是因为 namedtuple 是继承自 Python 内置的元组的子类,并且在实现上使用了C 语言。但是,namedtuple相对于普通元组的性能可能会略低,这是因为 namedtuple 对象需要存储额外的字段名信息,这些信息需要占用额外的内存空间。在处理大量的元组数据时,这些额外的内存开销可能会导致性能下降。另外,在创建具名元组时,由于需要解析和生成类定义,因此可能会花费更多的时间。但是,在大多数情况下,这种额外的开销是可以忽略不计的。
需要注意的是,对于大多数应用程序来说,namedtuple 与普通元组的性能差异非常小,因此在选择使用哪种类型时,应该根据具体的需求和代码可读性来做出选择。

元素的命名

在 namedtuple 中,元素的命名规则遵循 Python 标识符的命名规则,即必须以下划线或字母开头,后面可以是任意字母、数字或下划线。除此之外,元素的命名不能与 Python 关键字或保留字相同,例如 class、def 等。

ValueError

虽然 Python 的变量命名规则允许标识符以下划线开头,但具名元组禁止人为将以下划线开头的名称用于具名元素的命名。否则,Python 将抛出如下类似错误:

ValueError: Field names cannot start with an underscore: '_xxx'
私有属性或方法

在Python中,下划线作为前缀具有特殊含义,常用于表示私有变量或方法。当使用一个下划线开头的标识符时,Python解释器会默认将其视为私有变量或方法,即表示这个标识符应该被视为模块、类或对象的内部实现细节,并且不应该被外部代码直接使用。
因此,具名元组禁止使用以下划线开头的元素名,是为了保持代码的一致性和可读性。如果允许以下划线开头的元素名,会让人误以为这些元素是私有的,而实际上它们并不是,因为在具名元组中所有元素都是公开的。

小试牛刀

举个栗子

from collections import namedtuple# 定义一个具名元组类
Abstraction = namedtuple('Abstraction', ['name', 'age', 'sex'])
# 将具名元组实例化
twoMoons = Abstraction('RedHeart', 18, 'male')# 输出具名元组实例 twoMoons
print(twoMoons)
# 遍历具名元组中的每一个元素
for el in twoMoons:print(el)
# 通过索引来选择目标元素
print(twoMoons[0], twoMoons[1], twoMoons[2], sep='\\t')
# 通过元素名称来选择目标元素
print(twoMoons.name, twoMoons.age, twoMoons.sex, sep='\\t')
# 通过切片来选中目标元素
print(twoMoons[:2])

执行效果

Abstraction(name='RedHeart', age=18, sex='male')
RedHeart
18
male
RedHeart	18	male
RedHeart	18	male
('RedHeart', 18)

namedtuple

参数

namedtuple函数是Python标准库collections模块中提供的一个工厂函数,用于创建具名元组(named tuple)。

namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

其中:

项目 描述
typename 用于指定创建的具名元组整体的名称,类型为字符串。
field_names 用于指定具名元组中各个元素的名称,可以是以逗号分隔的字符串或是包含各个字段名称的可迭代对象,例如列表或元组。
rename 用于指定是否需要在指定的元素名称不可用时对元素进行重命名,默认值为 False,表示不进行重命名。如果该值为 True, 无效的元素名称会自动转换成 _索引 格式的名称。比如 [‘abc’, ‘def’, ‘ghi’, ‘abc’] 转换成 [‘abc’, ‘_1’, ‘ghi’, ‘_3’] , 消除了关键词 def 和重复字段名 abc
defaults 用于指定具名元组中各个字段的默认值,类型为可迭代对象,默认值为 None。当参数 defaults 的值为默认值 None 时,具名元组将不具有默认值。
module 用于指定具名元组所在的模块名称,类型为字符。

注:

  1. 参数列表中的 * 表明该符号后面的参数只能通过关键字参数的方式进行传递,而该符号前的参数可以通过位置参数或关键字参数的方式进行传递。对此,请参考如下示例:
from collections import namedtupleAbstraction = namedtuple('Abstraction', ['name', 'age', 'sex'])
# 尝试通过位置参数向参数 rename 传递值 True
twoMoons = Abstraction('RedHeart', 18, 'male', True)print(twoMoons)

执行效果

由于在 Abstaction 实例化为具名元组对象时将自动传递一个位置参数给 Abstraction.new() ,所以错误信息中包含 takes 4 positional arguments but 5 were given 而不是
takes 3 positional arguments but 4 were given

TypeError: Abstraction.__new__() takes 4 positional arguments but 5 were given
  1. 在参数列表中使用 * 符号后,该符号前的参数仍然可以通过关键字参数的形式进行传递。对此,请参考如下示例:
from collections import namedtupleAbstraction = namedtuple('Abstraction', ['name', 'age', 'sex'])
twoMoons = Abstraction(name='RedHeart', age=18, sex='male')
print(twoMoons)

执行效果

Abstraction(name='RedHeart', age=18, sex='male')
  1. 具名元组中的元素的名称可以为 namedtuple 函数中存在的形参名,并且在通过关键字参数为元素命名时并不会对 namedtuple 函数中的形参的值造成影响。对此,请参考如下示例:
from collections import namedtupleAbstraction = namedtuple('Abstraction', 'def rename name', rename=True)
twoMoons = Abstraction(1, rename=False, name='RedHeart')
print(twoMoons)

执行效果

虽然在实参列表中指明了 rename=False ,但仅会影响元素名称 rename 的值。

Abstraction(_0=1, rename=True, name='RedHeart')

rename

rename 参数 用于指定是否需要在指定的元素名称不可用时对元素进行重命名,默认值为 False,表示不进行重命名。如果该值为 True, 无效的元素名称会自动转换成 _索引 格式的名称。对此,请参考如下示例:

from collections import namedtuple# 使用非法名称作为具名元组的元素名称
# 并将 rename 参数设置为 True
Abstraction = namedtuple('Abstraction', 'abc def ghi abc]', rename=True)
twoMoons = Abstraction(1, 2, 3, 4)
print(twoMoons)

执行效果

Abstraction(abc=1, _1=2, ghi=3, _3=4)

注:

如果参数 rename 的值为 False ,那么在元素名称不符合要求时,Python 将抛出 ValueError 错误。对此,请参考下述代码:

from collections import namedtuple# 使用非法名称作为具名元组的元素名称
# 并将 rename 参数设置为 False
Abstraction = namedtuple('Abstraction', 'abc def ghi abc', rename=False)
twoMoons = Abstraction(1, 2, 3, 4)
print(twoMoons)

执行效果

由于元素名称使用了 Python 内置的关键字 def ,这不符合规范,因此 Python 抛出了 ValueError 异常。

ValueError: Type names and field names cannot be a keyword: 'def'

defaults

defaults 参数用于指定具名元组中各个字段的默认值,类型为可迭代对象,默认值为 None。对此,请参考如下示例:

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex', defaults=(18, 'female'))
twoMoons = Abstraction('RedHeart', sex='male')
print(twoMoons)

执行效果

将 Abstraction 实例化为对象以创建具名元组,由于仅指定了名为 name 及 sex 的元素的值,因此 age 元素将使用默认值(前提是
使用关键字参数 default 定义了这些元素的默认值,否则 Python 将抛出错误)。

Abstraction(name='RedHeart', age=18, sex='male')

注:

  1. 默认值将优先应用于元素名称整体中位于右侧的元素。如果元素名称整体为 [‘x’, ‘y’, ‘z’] ,默认值序列为 (1, 2) ,那么 x 就必须指定一个参数值 ,并且 y 的默认值为 1z 的默认值为 2

  2. 当参数 defaults 的值为默认值 None 时,具名元组将不具有默认值。

namedtuple 对象中的常用属性及方法

总览

namedtuple 对象的常用属性及方法

项目 描述
_fields 返回由具名元组中所有元素的名称所组成的元组。
_field_defaults 返回由元素名称为键,元素的默认值为值的字典。
_make() 该函数接收一个可迭代对象作为实参,返回一个使用可迭代对象中的值初始化的具名元组对象。
_asdict() 将具名元组对象转换为其对应的字典对象。返回的字典对象中,键为具名元组中的元素的名称,值为具名元组中该名称所对应的元素。
_replace(**kwargs) 该函数接收数个关键字参数。返回一个新的元组并将指定名称的元素的值进行更新。

为什么 namedtuple 对象的方法及属性多以 _ 为前缀?

具名元组对象的方法多带有 _ 前缀是为了避免和元素名称冲突。具名元组的元素名称在内部实现中也被当作属性名使用,因此如果方法名称与元素名称相同,则会发生名称冲突。为了避免这种情况,方法名称前面加上 _ 前缀,可以减少名称冲突的可能性。

_fields

使用具名元组对象的 _fields 属性,你将能够得到由具名元组中所有元素的名称所组成的元组。对此,请参考如下示例:

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex')
twoMoons = Abstraction('RedHeart', 18, 'male')
print(twoMoons._fields)

执行效果

('name', 'age', 'sex')

_field_defaults

使用具名元组对象的 _fields 属性,你将能够得到由元素名称为键,元素的默认值为值的字典。对此,请参考如下示例:

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex')
twoMoons = Abstraction('RedHeart', 18, 'male')
print(twoMoons._field_defaults)Abstraction = namedtuple('Abstraction', 'name age sex', defaults=[1, 2, 3])
twoMoons = Abstraction('RedHeart', 18, 'male')
print(twoMoons._field_defaults)

执行效果

{}
{'name': 1, 'age': 2, 'sex': 3}

_make(iterable)

虽然使用 namedtuple 定义的类可以直接创建具名元组对象,但是有些情况下,我们可能会需要从一个可迭代对象(比如列表、元组等)创建具名元组对象,这时候可以使用 _make() 方法。_make() 方法接受一个可迭代对象作为参数,返回一个使用该可迭代对象中的值初始化的具名元组对象。对此,请参考如下示例:

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex')
# 通过序列或可迭代对象实现具名元组对象的创建
twoMoons = Abstraction._make(['RedHeart', 18, 'male'])
print(twoMoons)

执行效果

Abstraction(name='RedHeart', age=18, sex='male')

_asdict()

使用具名元组对象的 _asdict() 方法能够将具名元组对象转换为其对应的字典对象。返回的字典对象中,键为具名元组中的元素的名称,值为具名元组中该名称所对应的元素。

举个栗子

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex')
twoMoons = Abstraction('RedHeart', 18, 'male')# 使用具名元组对象的 _asdict() 方法将该对象转换为有序字典
t_dict = twoMoons._asdict()
print(t_dict)
print(type(t_dict))

执行效果

{'name': 'RedHeart', 'age': 18, 'sex': 'male'}
<class 'dict'>

注:

  1. Python 3.1 版本及后续版本中,使用具名元组对象的 _asdict()
    方法返回的值将是一个 OrderedDict(有序字典) 而不是 dict(无序字典)

  2. Python 3.8 版本及后续版本中,使用具名元组对象的 _asdict()
    方法返回的值将是一个 dict 而不是 OrderDict。因为自 Python 3.7 版本开始,常规字典以及能够记住字典元素的插入顺序了。 如果需要 OrderedDict 提供的额外特性,可以通过 OrderedDict() 将返回结果转换为有序数组对象。

_replace(**kwargs)

使用具名元组对象的 _asdict() 方法将返回一个新的元组并将指定名称的元素的值进行更新。对此,请参考如下示例:

from collections import namedtupleAbstraction = namedtuple('Abstraction', 'name age sex')
twoMoons = Abstraction('RedHeart', 18, 'male')
# 输出 twoMoons 所处的内存地址
print(id(twoMoons))# 即使未对具名元组中的元素进行更新,
# _replace() 方法也将返回一个新的具名元组对象
updateMoons = twoMoons._replace()
# 输出 updateMoons 所处的内存地址,若
# 该地址与 id(twoMoons) 的输出不同,则
# 表明 twoMoons 与 updateMoons 是不同的对象
print(id(updateMoons))print(twoMoons._replace(name='BinaryMoons'))
print(twoMoons._replace(age=1000, sex='male'))

执行效果

2102119159568
2102119159728
Abstraction(name='BinaryMoons', age=18, sex='male')
Abstraction(name='RedHeart', age=1000, sex='male')

注:

具名元组对象的 _replace() 函数仅支持使用关键字参数进行值的传递,否则,Python 将抛出 TypeError 错误。

英语口语听力