namedtuple 一直存在,1随着时间的推移,它的便利性使它的使用远远超出了最初的预期用途。随着数据类现在覆盖了这些用例的一部分,应该使用命名元组来做什么?在本文中,我们将通过一些来自真实代码的示例来具体说明这一点。自 Python 2.6 起,namedtuple 就存在于标准库中,并允许构建元组子类,这些子类也具有可通过属性查找访问的字段。命名元组对于将字段名称分配给 csv 或 sqlite3 模块返回的结果元组特别有用。快速而肮脏的临时数据结构,比普通元组和常规类更具可读性(您可以免费获得构造函数关键字参数和 __repr__)可散列实例(用作 dict 键或设置成员,或用作装饰函数的参数,例如 functools .lru_cache)
数据类是在 Python 3.7 中添加的,通过生成所需的特殊方法,它允许像编写常规类一样轻松。使用冻结实例,它甚至涵盖可散列和不可变实例。在数据类之前,命名元组用于最后三个用例,因为标准库中没有其他好的替代方案——您可以使用普通的类定义来完成,但您必须手动编写所有特殊方法。我使用 Typing.NamedTuple 是因为它看起来类似于数据类;结果与传统 collections.namedtuple 工厂的结果相同。 >>> 类 Point ( NamedTuple ): ... x : int ... y : int ... >>> p = Point ( 1 , y = 2 ) >>> p Point(x=1, y=2 ) >>> p。 x 1 >>> p [ 0 ] 1 >>> list ( p ) [1, 2] >>> @dataclass ... class Point : ... x : int ... y : int ... >> > p = Point ( 1 , y = 2 ) >>> p Point(x=1, y=2) >>> p 。 x 1 >>> p [ 0 ] Traceback(最近一次调用):... TypeError: 'Point' 对象不可下标 >>> list (p) Traceback(最近一次调用):... TypeError: ' Point' 对象不可迭代实例总是可迭代的;这会使添加字段变得困难,因为添加新字段会破坏使用解包的代码。此外,如果在向后兼容的 API 中用作返回值,则意味着结果必须永远保持可迭代/可索引,即使您后来停止使用命名元组。有了上面提到的缺点,并且数据类涵盖了很多(可能是非预期的)用例,命名元组不再适用于任何东西了吗?
成对的东西,比如 HTTP 标头(字典并不总是合适的,因为同一个标头可以出现不止一次,并且在某些情况下顺序很重要)例如,对于我的提要阅读器库,我使用一个命名元组来建模结果一个提要更新,一个(提要 URL、更新详细信息或例外)对。这使得在交互式会话或调试时更容易理解值的含义;比较命名和未命名版本:此外,不同的类允许拥有用户可以使用 help() 查看的文档字符串,以及通过属性更好的语义(错误/好的在这里)。您已经在使用元组,并希望使新代码更具可读性:namedtuple 为您提供了这一点,但保证您不会破坏旧代码。有些人认为,无论你返回一个重要的元组,你都应该返回一个命名元组。我倾向于同意。例如,在我的提要阅读器库中,我使用一个命名元组来对与过滤相关的参数进行分组,因为它们有很多,并且它们在使用之前会被传递很多(我在这里更详细地介绍了原因)。
我知道所有的参数都应该被处理,所以我特别使用解包,因为我希望代码在添加新的时失败——如果我使用属性访问,代码会默默地成功。这不能替代测试,但早期警告是很好,尤其是在更大的代码库中。最后但并非最不重要的是,如果您关心内存或速度,命名元组很有用;它们比等效的(数据)类小得多,速度也快得多。在大多数情况下,差异并不重要,但如果您创建数百万个实例,差异就会变得明显。 size 是对象本身的 sys.getsizeof 加上它的 __dict__ (如果有),不包括实际值。总大小包括由 Pympler 的 asizeof 返回的值。当增加字段数时,obj.a 保持不变,而其他时间按比例增加。槽数据类总是比命名元组小 8 个字节。对于数据对象行,我使用了recordclass,它提供了dataclass/namedtuple-equivalent types.没有gc的版本不参与循环垃圾收集,所以它不应该用于递归数据结构。不过,该库仍然有一些粗糙的地方:文档有点混乱,我不得不使用(尚未发布的)0.15 版本才能使其正常工作;另外,请注意错误的总大小(它可能是 Pympler 错误)。然而,这些数字非常引人注目,如果您遇到这个问题,绝对值得一看。从输入导入 NamedTuple 从数据类导入数据类从记录类导入数据对象类 NT (NamedTuple): a : int b : int @dataclass (frozen = True ) class DC : a : int b : int @dataclass (frozen = True ) class DS : a : int b : int __slots__ = ( 'a' , 'b' ) class DO ( dataobject ): a : int b : int __options__ = dict ( readonly = True , fast_new = True ) class DG ( dataobject ): a : int b : int __options__ = dict ( readonly = True , fast_new = True , gc = True )
将您的电子邮件放在下面的框中,我会将新内容直接发送到您的收件箱!