例如,有几种标准方法可以使类成为可散列的(从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())现在假设我有很多想要hashable的类。它们都是不可变的,具有不可变的属性,批量散列所有这些属性是可以接受的(对于拥有太多属性的类,我们只需要散列一些足以避免大多数冲突的属性)。我可以避免为每个类手工编写__key()方法吗?
创建一个为它们定义__key()、__eq__和__hash__的基类是不是一个好主意?特别是,我不确定是否可以找到应该放入__hash__中的所有实例属性。我知道这是generally impossible,但在这种情况下,我们可以更多地假设对象是不可变的(例如,它是不可变的--在__init__完成后,它的属性都是可散列的,等等)。
(如果继承层次结构不起作用,也许装饰器会起作用?)
发布于 2012-09-20 20:30:27
实例将其属性存储在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__方法,这将成为继承所有不可变类的一个很好的基类。
发布于 2012-09-20 20:31:13
如果你假设你的属性有约定,你可以做到这一点。在您的示例中,这将是非常简单的,因为您的属性以"attr_“开头-所以您可以将您的__key方法编写为:
def __key(self):
return tuple (getattr(self, attr) for attr in self.__dict__ if attr.startswith("attr_") )正如你所看到的--你能找到的任何对生成器表达式过滤条件的测试都能满足你的需求。
我可以给你一个建议,让你的类使用Python的__slots__特性:这不仅会让你的属性名更容易找到,还会让你的imutable对象更高效地使用,并且占用更少的内存。
class X:
__slots__ = ("a", "b", "c")
def __key(self):
return tuple (getattr(self, attr) for attr in self.__class__.__slots__ )edit回答来自O.P.的第一条评论:
当然,这也适用于继承。如果你总是为它们使用对象的所有属性,你就不需要表达式中的" If“部分--在层次结构的顶端,将函数写成_key (而不是在内部为每个类提供唯一名称的__key ),它将适用于你的所有类。
https://stackoverflow.com/questions/12512511
复制相似问题