为何Python中namedtuple性能不及普通类?附测试代码
你做的这个性能测试很有意思——确实在属性访问这类高频操作上,namedtuple会比带__slots__的自定义类慢一些,核心原因在于两者的底层实现逻辑差异:
属性访问的路径复杂度不同
namedtuple本质是继承自tuple的子类,它的属性访问是通过动态映射实现的:当你访问s.price时,Python会调用namedtuple类的__getattribute__方法,这个方法会先把属性名(比如price)转换成对应的元组索引(比如1),再从元组的内存位置取值。而带__slots__的普通类,属性是直接存储在固定的内存偏移位置上的,访问时不需要额外的名称→索引映射步骤,直接读取内存,路径更短,速度自然更快。额外的兼容性逻辑带来的开销
namedtuple为了兼顾元组的所有特性(比如可索引、可解包),内置了一堆额外方法,比如__getitem__、_asdict、_replace等。这些方法虽然你没直接调用,但Python在处理namedtuple的属性访问时,内部需要处理这些方法带来的逻辑分支,累积起来就会产生微小的性能损耗。而你定义的Stock2类只实现了最基础的__init__,没有多余的逻辑负担,运行时更轻量。不可变特性的隐性校验
namedtuple是不可变类型,为了保证属性不能被修改,Python在属性访问的过程中会做一些隐性的校验(比如禁止赋值的逻辑判断)。虽然这些校验非常轻量,但在你测试的这种百万级循环场景下,就会体现出明显的耗时差异。而你的Stock2类是可变的(没有限制属性修改),访问时不需要这些校验步骤。
顺便提一句:如果测试实例创建的性能,namedtuple反而可能更快——因为CPython中namedtuple的初始化逻辑是用C实现的,比纯Python写的__init__方法效率更高,但属性访问这块确实是带__slots__的普通类占优。
内容的提问来源于stack exchange,提问作者skgbanga

