如何在 Python 中创建不可变对象? [英] How to make an immutable object in Python?
问题描述
虽然我从来不需要这个,但让我震惊的是,在 Python 中创建一个不可变的对象可能有点棘手.你不能只是覆盖 __setattr__
,因为那样你甚至不能在 __init__
.子类化元组是一个有效的技巧:
Although I have never needed this, it just struck me that making an immutable object in Python could be slightly tricky. You can't just override __setattr__
, because then you can't even set attributes in the __init__
. Subclassing a tuple is a trick that works:
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
但是你可以通过self[0]
和self[1]访问
a
和b
变量代码>,这很烦人.
But then you have access to the a
and b
variables through self[0]
and self[1]
, which is annoying.
这在纯 Python 中可行吗?如果没有,我将如何使用 C 扩展来实现?
Is this possible in Pure Python? If not, how would I do it with a C extension?
(仅适用于 Python 3 的答案是可以接受的).
(Answers that work only in Python 3 are acceptable).
更新:
从 Python 3.7 开始,要走的路是使用 @dataclass
装饰器,查看新接受的答案.
As of Python 3.7, the way to go is to use the @dataclass
decorator, see the newly accepted answer.
推荐答案
使用冻结的数据类
对于 Python 3.7+,您可以使用 数据类 带有 frozen=True
选项,这是一种非常pythonic 且可维护的方式来做你想做的事.
Using a Frozen Dataclass
For Python 3.7+ you can use a Data Class with a frozen=True
option, which is a very pythonic and maintainable way to do what you want.
它看起来像这样:
from dataclasses import dataclass
@dataclass(frozen=True)
class Immutable:
a: Any
b: Any
由于数据类的字段需要类型提示,我使用了任何来自 typing
模块.
As type hinting is required for dataclasses' fields, I have used Any from the typing
module.
在 Python 3.7 之前,经常看到命名元组被用作不可变对象.这在很多方面都很棘手,其中之一是命名元组之间的 __eq__
方法不考虑对象的类.例如:
Before Python 3.7 it was frequent to see namedtuples being used as immutable objects. It can be tricky in many ways, one of them is that the __eq__
method between namedtuples does not consider the objects' classes. For example:
from collections import namedtuple
ImmutableTuple = namedtuple("ImmutableTuple", ["a", "b"])
ImmutableTuple2 = namedtuple("ImmutableTuple2", ["a", "c"])
obj1 = ImmutableTuple(a=1, b=2)
obj2 = ImmutableTuple2(a=1, c=2)
obj1 == obj2 # will be True
如您所见,即使obj1
和obj2
的类型不同,即使它们的字段名称不同,obj1 == obj2
仍然给出 True
.这是因为使用的 __eq__
方法是元组的方法,它只比较给定位置的字段的值.这可能是一个巨大的错误来源,特别是当您对这些类进行子类化时.
As you see, even if the types of obj1
and obj2
are different, even if their fields' names are different, obj1 == obj2
still gives True
. That's because the __eq__
method used is the tuple's one, which compares only the values of the fields given their positions. That can be a huge source of errors, specially if you are subclassing these classes.
这篇关于如何在 Python 中创建不可变对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!