我必须做什么才能使用我的自定义类型的对象作为Python字典中的键(其中我不希望“对象id”作为键),例如。
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
我想使用MyThing作为键,如果名称和位置相同,则认为它们相同。
从c# /Java,我习惯于覆盖和提供一个等于和hashcode方法,并承诺不改变hashcode所依赖的任何东西。
我必须在Python中做什么来实现这一点?我应该这样做吗?
(在一个简单的情况下,就像这里,也许它会更好,只是放置(name,location)元组作为键-但考虑到我想要的键是一个对象)
如果你想要特殊的哈希语义,你可以覆盖__hash__,为了使你的类可用作键,你可以覆盖__cmp__或__eq__。比较相等的对象需要具有相同的哈希值。
Python期望__hash__返回一个整数,不建议返回Banana():)
用户定义的类默认有__hash__,调用id(self),正如你所注意到的。
文档中有一些额外的提示:
Classes which inherit a __hash__()
method from a parent class but change
the meaning of __cmp__() or __eq__()
such that the hash value returned is
no longer appropriate (e.g. by
switching to a value-based concept of
equality instead of the default
identity based equality) can
explicitly flag themselves as being
unhashable by setting __hash__ = None
in the class definition. Doing so
means that not only will instances of
the class raise an appropriate
TypeError when a program attempts to
retrieve their hash value, but they
will also be correctly identified as
unhashable when checking
isinstance(obj, collections.Hashable)
(unlike classes which define their own
__hash__() to explicitly raise TypeError).
你需要添加2个方法,注意__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文档定义了这些关键对象的要求,即它们必须是可哈希的。
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
我注意到在python 3.8.8(可能更早)中,你不再需要显式地声明__eq__()和__hash__()来有机会在dict中使用自己的类作为键。
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可以自己管理它,但是我可能错了。