当类仅存储不可变字段、无方法、不继承、不需动态属性时,应选namedtuple;它内存小、创建快、支持解包和索引,但不可赋值,误用赋值或实例方法会报错。

什么时候该用 namedtuple 而不是普通类?
当你的类只用来存几个不可变字段、不带方法、不需要继承、也不需要动态添加属性时,namedtuple 就是更轻量且语义更清晰的选择。它本质是 tuple 的子类,内存占用小、创建快、支持解包和索引访问,但一旦实例化就不能改值——这点和 dataclass(frozen=True) 类似,但比后者启动开销低。
常见误用场景:想在 namedtuple 实例上赋值(比如 obj.x = 1)或调用自定义方法,结果抛出 AttributeError 或 TypeError。
namedtuple 的基本定义和字段访问方式
用 collections.namedtuple 创建类型时,字段名必须是合法标识符,不能以数字开头,也不能是 Python 关键字(如 class、def)。定义后得到的是一个类工厂,不是实例。
- 定义:
Point = namedtuple('Point', ['x', 'y'])或Point = namedtuple('Point', 'x y') - 实例化:
p = Point(1, 2),不能用关键字参数以外的方式传参(除非用_make()) - 访问字段:
p.x、p[0]、getattr(p, 'y')都可以;但p.x = 3会报AttributeError - 字段名冲突时会自动重命名(如传入
['def', 'class']),可通过rename=True参数启用,此时生成的字段名为_0、_1
如何给 namedtuple 加点“类味儿”?
原生 namedtuple 不支持方法,但你可以通过继承 + 类属性方式扩展,前提是不破坏其不可变性:
- 定义方法只能是
@staticmethod或@classmethod,不能是实例方法(因为没地方存self状态) - 可以加
__doc__、__slots__ = ()(虽然默认已冻结)、甚至重载__repr__(但要小心别引入可变逻辑) - 更稳妥的做法是把逻辑抽到外部函数,比如
def distance(a: Point, b: Point) -> float:,这样语义更干净 - 如果真需要实例方法和默认值,直接换用
dataclasses.dataclass(Python 3.7+)或typing.NamedTuple(支持类型注解和默认值)
和 typing.NamedTuple 比有什么区别?
typing.NamedTuple 是类型提示友好的替代写法,语法更现代,支持类型注解和字段默认值,底层仍基于 namedtuple,行为一致:
- 写法:
class Point(NamedTuple): x: int; y: int = 0 - 默认值只允许在末尾字段设置,否则会报
TypeError: non-default argument follows default argument - 类型信息仅用于静态检查(mypy/pylance),运行时不校验;字段依然不可变
- 不能用
Point._replace(x=5)修改带默认值的字段以外的字段——这和普通namedtuple行为一致
如果你项目已启用类型检查,优先用 typing.NamedTuple;如果还要兼容旧版本 Python 或追求最小依赖,就用 collections.namedtuple。
最常被忽略的一点:所有字段名都会变成实例的属性,但不会出现在 __dict__ 中——所以基于 __dict__ 的序列化(如 vars())会失败,得用 _asdict() 或手动构造字典。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











