如何在 Python 中创建不可变对象?

新手上路,请多包涵

虽然我从来不需要这个,但让我吃惊的是,在 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 许可协议

阅读 433
2 个回答

使用冻结的数据类

对于 Python 3.7+,您可以使用带有 frozen=True 选项数据类,这是一种非常 pythonic 和可维护的方式来做你想做的事。

它看起来像这样:

 from dataclasses import dataclass

@dataclass(frozen=True)
class Immutable:
    a: Any
    b: Any

由于数据类字段 需要类型提示,因此我使用 typing 模块 中的 Any。

不使用 Namedtuple 的原因

在 Python 3.7 之前,命名元组经常被用作不可变对象。它在很多方面都可能很棘手,其中之一是命名元组之间的 __eq__ 方法不考虑对象的类。例如:

 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

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 。那是因为 __eq__ 使用的方法是元组的方法,它只比较给定字段位置的字段值。这可能是一个巨大的错误来源,特别是如果您正在对这些类进行子类化。

原文由 Jundiaius 发布,翻译遵循 CC BY-SA 4.0 许可协议

我刚刚想到的另一个解决方案:获得与原始代码相同的行为的最简单方法是

Immutable = collections.namedtuple("Immutable", ["a", "b"])

它没有解决可以通过 [0] 等访问属性的问题,但至少它相当短,并且提供了与 picklecopy

namedtuple 创建一个类似于我在 这个答案 中描述的类型,即派生自 tuple 并使用 __slots__ 它在 Python 2.6 或更高版本中可用。

原文由 Sven Marnach 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题