如何在 Python 中创建不可变对象? [英] How to make an immutable object in Python?

查看:30
本文介绍了如何在 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]ab变量代码>,这很烦人.

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

如您所见,即使obj1obj2的类型不同,即使它们的字段名称不同,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆