创建不可变类的Python方式是什么?

时间:2019-05-31 03:37:05

标签: python object immutability

我目前正在使用Python处理某些数据结构,我希望它们是可哈希化的(因此它们可以存储在字典和集合中)。到目前为止,我已经看到了三种主要方法:

①创建一个普通的类,并为其提供一个__hash__方法。

class DataStructure:
    def __init__(self, member):
        self.member = member
    def __hash__(self):
        return hash(self.member)
    def __eq__(self, other):
        return isinstance(other, DataStructure) and self.member == other.member

但这只是“信任”最终用户而不会使其突变member。如果他们在将它存储在集合或字典中时确实对其进行了变异,则会导致Bad Things。

②使用collections.namedtuple

DataStructure = collections.namedtuple('DataStructure', ('member',))

但是namedtuple不能具有成员函数,而拥有成员函数真的很好。

③使用__slots__并覆盖__setattr__,如图here所示。

class DataStructure:
    __slots__ = ['member']
    def __init__(self, member):
        super(DataStructure, self).__setattr__('member', member)
    def __setattr__(self, key, value):
        raise ValueError('Mutating this object is Not Allowed')
    # also define __hash__ and __eq__ here

但是这个doesn't seem to be intended usage,也使继承更加复杂。最重要的是,它感觉不到“ Pythonic”。

因此:在Python中创建不可变类的首选方法是什么?希望自定义数据结构可哈希化似乎并不奇怪,这就是__hash__的原因毕竟存在。而且,我宁愿实际上使该类不可变,而不是告诉用户“您可以自由分配给这些成员,但是如果这样做,您的集合和字典可能会崩溃并烧毁” — Python通常试图不让人们使它们成为现实。首先出现错误(这就是setfrozenset不同的原因。

2 个答案:

答案 0 :(得分:2)

绝对不变性可能无法实现,但是您可以使用properties来表明自己的意图:

class DataStructure:
    def __init__(self, member):
        self._member = member
    def __hash__(self):
        return hash(self.member)
    def __eq__(self, other):
        return isinstance(other, DataStructure) and self.member == other.member
    @property
    def member(self):
        return self._member

通过这种方式member被公开为“公共”属性,但不可写:

>>> ds = DataStructure(42)
>>> ds.member
42
>>> ds.member = 56
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

坚定的最终用户仍然可以对self._member进行突变,但是此时您希望他们知道他们做错了事。

答案 1 :(得分:1)

在要成为“只读”变量的前面加上“ _”。仍然可以从类内部进行更改,但是IDE无法预测它,如果您尝试从类外部进行更改,则会发出警告。

__slots__ = ('_member',)