自定义类型作为字典键

242 投票
6 回答
140602 浏览
提问于 2025-04-16 11:15

我想知道,怎样才能在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中,我该怎么做才能实现这个呢?我真的需要这样做吗?

(在简单的情况下,比如这里,或许直接用一个(name, location)的元组作为键会更好,但我希望键是一个对象。)

6 个回答

23

如果你想让你的类有特别的哈希功能,就需要重写 __hash__ 方法;如果你想让你的类可以用作键值,就需要重写 __cmp____eq__ 方法。那些比较相等的对象,它们的哈希值也必须相同。

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

用户自定义的类默认会有一个 __hash__ 方法,这个方法会调用 id(self),正如你所提到的。

这里有一些来自 文档 的额外提示:

如果一个类从父类继承了 __hash__() 方法,但改变了 __cmp__()__eq__() 的含义,使得返回的哈希值不再合适(比如,改为基于值的相等概念,而不是默认的基于身份的相等),那么可以通过在类定义中设置 __hash__ = None 来明确标记自己为不可哈希。这样做的结果是,当程序尝试获取它们的哈希值时,类的实例会抛出合适的 TypeError,同时在检查 isinstance(obj, collections.Hashable) 时,它们也会被正确识别为不可哈希(这和那些定义了自己的 __hash__() 方法并明确抛出 TypeError 的类不同)。

39

在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
289

你需要添加两个方法,分别是__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的字典文档中说明了对键对象的要求,也就是说,它们必须是可哈希的

撰写回答