有几种标准方法可以使类具有哈希性,例如(借用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__的所有实例属性。我知道这通常是不可能的,但是在这种情况下,我们可以对对象进行更多的假设(例如,它是不可变的-在__init__完成之后,它的属性都是可哈希的,等等)。

(如果继承层次结构不起作用,可能是装饰器会起作用?)

遗产对我来说很好…

我也继承遗产。

实例将其属性存储在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()调用的不可变序列。

对于更复杂的设置,您必须测试EDOCX1(跳过函数等)返回的类型,或者使用特定的属性模式,或者重新调整__slots__的用途以列出可以使用的适当属性。

再加上您的__hash__和__eq__方法,对于所有不变的类来说,这将是一个很好的基类来继承。

CAN __key是self.__dict__.values()回报,没有转换到tuple???????的观点面向看来是哈希。

不,我需要的那本ensure阶的属性,在self.__dict__.values()洽是???????因为我的__key是用于非只读的散列方法,但也为__eq__,我不能还请得起的风险__key()校正的同一值的两instances不等。我不想有任何的担保这一本字典的迭代中的一阶的,如果它的创造中的一阶。(和在的事实,它可能不会是创造了在一阶…………………__init__可能跟一个不同的分支中的一些instances致因,一个不同的属性赋值的阶)。

"我已经添加到排序,ensure稳定的顺序.

"最大的一次,你创建一个字典,它的迭代中的一阶(这一次)As Long As你不添加更多的键/值。现在的阶,pypy使用的可能是不同的,比Cpython,或它可能是不同的,使用python2.6 VS python2.7……但这不必真的t物…………………

但当我测试x.__key() == y.__key()(平等),I将是比较两个不同的__dict__对象,不是同一的,因为每一审有其自身的__dict__。。。。。。。我好奇的,这不担保extend到不同的dictionaries在同样的会议,在insertion阶是一样的吗?在任何情况下,我不能担保那insertion阶是同样的在这里。

"mgilson:它也depends的阶的附加在你的钥匙。如果两个碰撞开关的钥匙,他们中阶词典太型的insertion阶。

"martijnpieters……是的,但是那页为什么我说一次"你创建一个字典…………………As Long As你不添加更多的键/值"。我的意思是,如果你有一个dict和迭代过它现在才过它以后的迭代中的代码,它会屈服在同阶元素As Long As你不添加到它的细胞之间的关系。

"马克斯-如果你不能保证insertion阶,那么你需要sorted算计。如果你能保证这insertion阶方法都是一样的dictionaries,那么我想你可以safely旁路的排序…………………尽管从我那我们可能CPython的特异性(我不是阳性的,文件包中的阶是洽跨不同的dictionaries以同样的钥匙和insertion阶)

"mgilson:是的,但是我怎么能在某些运算是对程序集是总是在每一阶方法的实例吗?它太容易的创建错误relying对采购部词典,更好的分类。

"……我们martijnpieters I它甚多depends的类。如果对象是immutable,一切都是那么supposedly弹出式后__init__是t它???????如果程序集是在一阶在__init__洽,不在insertion阶在__dict__是一样的吗?……然而,y-o-u-r-e右键,它是更安全的的分类。但是,这也是一个O(N logN)操作时,每一个你想要的哈希,这可能杀死你的效率,使用本词典作为一个关键设计实例…………………(不可能的吗?)

"mgilson:唐underestimate的权力if:/ elif:等。在__init__逻辑。属性设置的另一个能阶的组合物。除非在数字是惊人的大,我就不担心关于性能的方法__key,真的。

"martijnpieters -是的,你会definitely需要非常小心的是与if/ elif的逻辑。我认为,如果我们从suffered绩效排序,因为一批大学的属性(或很多电话的__hash__),你可以把calculate的哈希值的尽头__init__和有回报的那__hash__属性为好。I我们y-o-u-r-e右键。早优化是几乎总是一个坏主意…………………

作为一个边注,我不讨论启动这个"说不"的那一__key使用sorted(它应该)。一开始它的信息关于如何dictionaries OP工作…………………好吧,我只是在让讨论继续,因为我的思想我最好学习一些关于创建哈希类由于这不是我做的东西很常常准备。

如果为属性假定约定,则可以这样做。在你的例子中,这将是非常简单的,因为您的属性以"attr_uuu"开头,所以您可以将uu键方法编写为:

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的形式在内部为每个类创建唯一的名称),它将适用于所有类。

如果您要基于__dict__,为什么不直接使用__dict__.items()或__dict__.values(),直接从实例字典中获取属性值?

谢谢。不幸的是,我正在使用现有的代码体。我希望在不做太多更改的情况下,使一些类可以散列。我想是可行的,但有点费时。