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

Python 自定义类型的对象作为字典键,python,dictionary,Python,Dictionary,我必须做什么才能在Python字典中将自定义类型的对象用作键(我不希望“对象id”充当键),例如 如果名称和位置相同,我想使用MyThing作为被认为相同的键。 从C#/Java开始,我习惯于重写并提供equals和hashcode方法,并承诺不修改hashcode依赖的任何内容 我必须在Python中做些什么才能实现这一点?我甚至应该吗 (在一个简单的例子中,也许最好把一个(名称,位置)元组作为关键字,但是考虑到我希望这个键是一个对象) 如果你想要特殊的哈希语义,而代码> >比较相等的对象需要

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

如果名称和位置相同,我想使用MyThing作为被认为相同的键。 从C#/Java开始,我习惯于重写并提供equals和hashcode方法,并承诺不修改hashcode依赖的任何内容

我必须在Python中做些什么才能实现这一点?我甚至应该吗


(在一个简单的例子中,也许最好把一个(名称,位置)元组作为关键字,但是考虑到我希望这个键是一个对象)

如果你想要特殊的哈希语义,而代码> >比较相等的对象需要具有相同的哈希值

Python希望
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

正如您所指出的,用户定义的类在默认情况下具有调用
id(self)
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

网站上还有一些额外的提示:

继承一个
\uuuuuuuuuuuuuuuu散列()
方法,但该方法已更改
\uuu cmp\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
这样,返回的哈希值
不再合适(例如
转换到基于价值的概念
相等而不是默认值
基于身份的平等)可以
明确地将自己标记为
通过设置
\uuuu散列\uuuu=None不可破坏
在类定义中。这样做
这意味着
这个班提出了一个适当的建议
当一个程序试图
检索它们的哈希值,但是
也将被正确识别为
检查时不可损坏
isinstance(obj,collections.Hashable)
(与定义自己的类不同
\uuuuu散列函数(
以显式引发TypeError)

您需要添加,注意
\uuuuu散列\uuuuuuu
\uuuuuuuu eq\uuuuu

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在关键对象上定义了这些要求,即它们必须是。

在Python 2.6或更高版本中,另一种选择是使用
集合。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

使用散列有什么问题?可能是因为他想要两个
虚构的
,如果它们有相同的
名称
位置
,来索引字典以返回相同的值,即使它们是作为两个不同的“对象”单独创建的。“也许最好只放置一个(名称、位置)元组作为密钥-但我想我想把密钥变成一个对象)“你是指:一个非复合对象?仅仅哈希是不够的,另外,你需要重写<代码>”、“EQGy< < /Cord>”或“<代码>但您可能还是希望覆盖它们以适应新的语义。@Skurmedel:是的,但尽管您可以调用
cmp
并对不覆盖这些方法的用户类使用
=
,其中一个必须实现,以满足提问者的要求,即具有相似名称和位置的实例具有相同的字典键。
hash(self.name)
看起来比
self.name.\uuuuuuuuu hash()
,如果您这样做,并且您可以这样做
hash((x,y))
以避免自己进行异或运算。另请注意,我刚刚发现,像那样调用
x.\uuuu hash\uuuu()
也是错误的,因为它会产生错误的结果:@Rosh Oxymoron:谢谢你的评论。在写作时,我使用显式
表示
\uuuueq\uuuuuu
,但后来我想“为什么不使用元组?”因为我经常这样做(我认为它更具可读性)。出于某种奇怪的原因,我的眼睛并没有回到关于
\uuuuu hash\uuuuuu
的问题上来。@user877329:您是否试图使用一些混合器数据结构作为键?显然,在某些repo中,某些对象要求您首先“冻结”它们以避免可变性(不允许对python字典中用作键的基于值的对象进行变异)@kawing chiu
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