有几种标准方法可以使类可以使用,例如(从SO借用):
# assume X has 2 attributes: attr_a and attr_b
class X:
def __key(self):
return (self.attr_a, self.attr_b)
def __eq__(x, y):
return isinstance(y, x.__class__) and x.__key() == y.__key()
def __hash__(self):
return hash(self.__key())
现在假设我有许多我想要制作的课程。它们都是不可变的,具有不可变属性,散列所有这些属性是可以接受的(对于具有太多属性的类,我们只想散列一些足以避免大多数冲突的属性)。我可以避免为每个班级手工编写__key()
方法吗?
创建一个为其定义__key()
,__eq__
和__hash__
的基类是不是一个好主意?特别是,我不确定是否可以找到应该进入__hash__
的所有实例属性。我知道这是generally impossible,但在这种情况下,我们可以假设更多关于对象(例如,它是不可变的 - 在__init__
完成后,它的属性都是可清除的等等。)
(如果继承层次结构不起作用,也许装饰者会这么做?)
答案 0 :(得分:4)
实例将其属性存储在self.__dict__
:
>>> class Foo(object):
... def __init__(self, foo='bar', spam='eggs'):
... self.foo = foo
... self.spam = spam
...
>>> f = Foo()
>>> f.__dict__
{'foo': 'bar', 'spam': 'eggs'}
如果您没有在实例上存储任何方法,则默认.__key()
可以是:
def __key(self):
return tuple(v for k, v in sorted(self.__dict__.items()))
我们按属性名称对项目进行排序; tuple()
调用确保我们返回适合hash()
调用的不可变序列。
对于更复杂的设置,您必须测试values()
返回的类型(跳过函数等)或使用特定的属性模式或重新调整__slots__
列出适当的属性可以使用。
与您的__hash__
和__eq__
方法一起构建一个良好的基类来继承所有不可变类。
答案 1 :(得分:1)
如果您假设属性的约定,则可以执行此操作。在你的例子中, 这将是非常微不足道的,因为你的属性以“attr_”开头 - 所以你可以将你的__key方法编写为:
def __key(self):
return tuple (getattr(self, attr) for attr in self.__dict__ if attr.startswith("attr_") )
正如您所看到的 - 您可以找到的任何测试都可以满足您的需求。
我可以给你的建议是让你的课程使用Python的__slots__
功能:
这样不仅可以轻松查找属性名称,还可以使您的可变对象使用效率更高,内存占用更少。
class X:
__slots__ = ("a", "b", "c")
def __key(self):
return tuple (getattr(self, attr) for attr in self.__class__.__slots__ )
修改强> 回答O.P。的第一条评论:
当然,这适用于继承。如果你总是使用它们的所有对象的属性,你不需要表达式的“if”部分 - 将函数写为_key
(而不是__key
,它为一个唯一的名称每个类在内部)在层次结构顶部的类上,它将适用于所有类。