自定义类型的对象作为字典键

时间:2011-02-04 18:51:40

标签: python dictionary

我必须做什么才能将自定义类型的对象用作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中做些什么来实现这一目标?我应该吗?

(在一个简单的例子中,就像这里一样,也许最好只将一个(名称,位置)元组作为键 - 但考虑我希望键是一个对象)

4 个答案:

答案 0 :(得分:200)

您需要添加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

答案 1 :(得分:30)

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

答案 2 :(得分:20)

如果您需要特殊的哈希语义,则覆盖__hash__,以及__cmp____eq__,以使您的类可用作键。比较相等的对象需要具有相同的哈希值。

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

默认情况下,用户定义的类会__hash__调用id(self),如您所述。

documentation提供了一些额外的提示:

  

继承__hash__()的类   父类的方法,但改变   __cmp__()__eq__()的含义   这样返回的哈希值就是   不再适合(例如通过   转向基于价值的概念   平等而不是默认   基于身份的平等)可以   明确地将自己标记为存在   通过设置__hash__ = None不可取消   在类定义中。这样做   意味着不仅是实例   班级提出了适当的   程序尝试时的TypeError   检索他们的哈希值,但他们   也将被正确识别为   检查时不可用   isinstance(obj, collections.Hashable)   (不像定义自己的类   __hash__()显式引发TypeError)。

答案 3 :(得分:0)

我注意到在 python 3.8.8(可能更早)中,您不再需要明确声明 __eq__()__hash__() 才能有机会使用自己的类作为 dict 中的键。< /p>

class Apple:
    def __init__(self, weight):
        self.weight = weight
        
    def __repr__(self):
        return f'Apple({self.weight})'

apple_a = Apple(1)
apple_b = Apple(1)
apple_c = Apple(2)

apple_dictionary = {apple_a : 3, apple_b : 4, apple_c : 5}

print(apple_dictionary[apple_a])  # 3
print(apple_dictionary)  # {Apple(1): 3, Apple(1): 4, Apple(2): 5}

我假设从某个时候开始 Python 会自行管理它,但我可能是错的。