Python 自定义类型的对象作为字典键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4901815/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Object of custom type as dictionary key
提问by Anonym
What must I do to use my objects of a custom type as keys in a Python dictionary (where I don't want the "object id" to act as the key) , e.g.
我必须做什么才能将自定义类型的对象用作 Python 字典中的键(我不希望“对象 ID”充当键),例如
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
I'd want to use MyThing's as keys that are considered the same if name and location are the same. From C#/Java I'm used to having to override and provide an equals and hashcode method, and promise not to mutate anything the hashcode depends on.
如果名称和位置相同,我想使用 MyThing 作为键。在 C#/Java 中,我习惯于必须重写并提供一个 equals 和 hashcode 方法,并承诺不会改变 hashcode 所依赖的任何内容。
What must I do in Python to accomplish this ? Should I even ?
我必须在 Python 中做什么才能做到这一点?我应该吗?
(In a simple case, like here, perhaps it'd be better to just place a (name,location) tuple as key - but consider I'd want the key to be an object)
(在一个简单的情况下,就像这里一样,也许最好只放置一个 (name,location) 元组作为键 - 但考虑到我希望键是一个对象)
采纳答案by 6502
You need to add 2 methods, note __hash__and __eq__:
您需要添加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)
The Python dict documentationdefines these requirements on key objects, i.e. they must be hashable.
回答by Skurmedel
You override __hash__if you want special hash-semantics, and __cmp__or __eq__in order to make your class usable as a key. Objects who compare equal need to have the same hash value.
__hash__如果您需要特殊的哈希语义,__cmp__或者__eq__为了使您的类可用作键,您可以覆盖。比较相等的对象需要具有相同的哈希值。
Python expects __hash__to return an integer, returning Banana()is not recommended :)
Python 期望__hash__返回一个整数,Banana()不推荐返回:)
User defined classes have __hash__by default that calls id(self), as you noted.
正如您所指出的,用户定义的类在__hash__默认情况下会调用id(self)。
There is some extra tips from the documentation.:
文档中有一些额外的提示。:
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__ = Nonein 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 checkingisinstance(obj, collections.Hashable)(unlike classes which define their own__hash__()to explicitly raise TypeError).
__hash__()从父类继承方法但更改__cmp__()或__eq__()使得返回的哈希值不再合适的类(例如,通过切换到基于值的相等概念而不是基于默认身份的相等)可以明确地将自己标记为通过__hash__ = None在类定义中设置是不可散列的。这样做意味着当程序尝试检索它们的哈希值时,类的实例不仅会引发适当的 TypeError,而且在检查时它们也会被正确识别为不可散列isinstance(obj, collections.Hashable)(与定义自己的类__hash__()以显式引发 TypeError 不同)。
回答by Sven Marnach
An alternative in Python 2.6 or above is to use collections.namedtuple()-- it saves you writing any special methods:
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

