虽然我从来不需要这个,但让我吃惊的是,在 Python 中制作一个不可变对象可能有点棘手。你不能只覆盖 __setattr__
,因为这样你甚至不能在 __init__
中设置属性。子类化元组是一个有效的技巧:
class Immutable(tuple):
def __new__(cls, a, b):
return tuple.__new__(cls, (a, b))
@property
def a(self):
return self[0]
@property
def b(self):
return self[1]
def __str__(self):
return "<Immutable {0}, {1}>".format(self.a, self.b)
def __setattr__(self, *ignored):
raise NotImplementedError
def __delattr__(self, *ignored):
raise NotImplementedError
But then you have access to the a
and b
variables through self[0]
and self[1]
, which is annoying.
这在 Pure Python 中可能吗?如果没有,我将如何使用 C 扩展来做到这一点?
(仅适用于 Python 3 的答案是可以接受的)。
更新:
从 Python 3.7 开始,要走的路是使用 @dataclass
装饰器,请参阅新接受的答案。
原文由 Lennart Regebro 发布,翻译遵循 CC BY-SA 4.0 许可协议
使用冻结的数据类
对于 Python 3.7+,您可以使用带有
frozen=True
选项 的 数据类,这是一种非常 pythonic 和可维护的方式来做你想做的事。它看起来像这样:
由于数据类字段 需要类型提示,因此我使用 了
typing
模块 中的 Any。不使用 Namedtuple 的原因
在 Python 3.7 之前,命名元组经常被用作不可变对象。它在很多方面都可能很棘手,其中之一是命名元组之间的
__eq__
方法不考虑对象的类。例如:As you see, even if the types of
obj1
andobj2
are different, even if their fields’ names are different,obj1 == obj2
still givesTrue
。那是因为__eq__
使用的方法是元组的方法,它只比较给定字段位置的字段值。这可能是一个巨大的错误来源,特别是如果您正在对这些类进行子类化。