2011-02-04 56 views
146

必须做什么才能将我的自定义类型的对象用作Python字典中的键(我不希望“对象ID”充当键)自定义类型作为字典键的对象

class MyThing: 
    def __init__(self,name,location,length): 
      self.name = name 
      self.location = location 
      self.length = length 

我想使用MyThing作为密钥,如果名称和位置相同,则认为它们是相同的。从C#/ Java我习惯于重写并提供一个equals和hashcode方法,并承诺不会改变hashcode依赖的任何东西。

我必须在Python中做什么来完成这个任务?我应该甚至?

(在简单的情况下,喜欢这里,或许会更好,只是放置一个(名称,位置)元组的关键 - 但考虑到我想关键是一个对象)

+0

有什么不对使用哈希? – 2011-02-04 18:55:18

+1

可能是因为他想要两个`MyThing`,如果它们具有相同的`name`和`location`,则索引字典以返回相同的值,即使它们是作为两个不同的“对象”单独创建的。 – Santa 2011-02-04 19:00:20

+1

“也许最好是将一个(名称,位置)元组作为关键元素 - 但考虑我希望键是一个对象)”您的意思是:一个非复合对象? – eyquem 2011-02-04 21:20:29

回答

168

您需要添加2 methods,注意__hash____eq__

class MyThing: 
    def __init__(self,name,location,length): 
     self.name = name 
     self.location = location 
     self.length = length 

    def __hash__(self): 
     return hash((self.name, self.location)) 

    def __eq__(self, other): 
     return (self.name, self.location) == (other.name, other.location) 

    def __ne__(self, other): 
     # Not strictly necessary, but to avoid having both x==y and x!=y 
     # True at the same time 
     return not(self == other) 

Python的dict documentation定义的关键对象这些要求,即他们必须hashable

18

你如果您想要特殊的哈希语义,请覆盖__hash__,并且__cmp____eq__以使您的类可用作密钥。比较相等的对象需要具有相同的散列值。

Python所预期__hash__返回一个整数,返回Banana()不推荐:)

用户定义的类有__hash__默认调用id(self),正如你指出。

有从documentation一些额外提示:

类从父类继承__hash__() 方法,但改变 的含义__cmp__()__eq__() ,使得返回的哈希值是 没有更适合(例如通过 切换到基于价值的概念 而不是默认的 基于身份的相等)可以将 明确地标记为是 通过在类定义中设置__hash__ = None 而不可设置。这样做 意味着,不仅会的 实例类的检查 isinstance(obj, collections.Hashable) 时(不像定义类抛出一个适当的 类型错误时的程序试图 检索其哈希值,但他们 也将被正确地识别为 unhashable他们自己的 __hash__()明确地引发TypeError)。

28

另一种在Python 2.6以上是使用collections.namedtuple() - 它可以节省你编写任何特殊的方法:

from collections import namedtuple 
MyThingBase = namedtuple("MyThingBase", ["name", "location"]) 
class MyThing(MyThingBase): 
    def __new__(cls, name, location, length): 
     obj = MyThingBase.__new__(cls, name, location) 
     obj.length = length 
     return obj 

a = MyThing("a", "here", 10) 
b = MyThing("a", "here", 20) 
c = MyThing("c", "there", 10) 
a == b 
# True 
hash(a) == hash(b) 
# True 
a == c 
# False