我目前正在使用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通常试图不让人们使它们成为现实。首先出现错误(这就是set
和frozenset
不同的原因。
答案 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',)