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

Ano*_*nym 179 python dictionary

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

class MyThing:
    def __init__(self,name,location,length):
            self.name = name
            self.location = location
            self.length = length
Run Code Online (Sandbox Code Playgroud)

如果名称和位置相同,我想使用MyThing作为被认为相同的键.从C#/ Java我习惯于必须覆盖并提供equals和hashcode方法,并承诺不会改变hashcode所依赖的任何内容.

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

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

650*_*502 210

你需要添加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)
Run Code Online (Sandbox Code Playgroud)

Python dict文档定义了对关键对象的这些要求,即它们必须是可清除的.

  • `hash(self.name)`看起来比`self.name .__ hash __()`好看,如果你这样做,你可以做`hash((x,y))`以避免自己异或. (17认同)
  • 另外需要注意的是,我刚刚发现调用`x .__ hash __()`就像是*错*,因为它_can_产生_incorrect_结果:http://pastebin.com/C9fSH7eF (5认同)
  • @user877329:您是否尝试使用某些搅拌机数据结构作为键?显然,在某些存储库中,某些对象要求您首先“冻结”它们以避免可变性(不允许改变已用作 python 字典中的键的基于值的对象) (2认同)

Sve*_*ach 32

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
Run Code Online (Sandbox Code Playgroud)


Sku*_*del 20

__hash__如果您需要特殊的哈希语义,__cmp__或者__eq__为了使您的类可用作键,您可以覆盖它.比较相等的对象需要具有相同的哈希值.

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

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

文档中还有一些额外的提示.:

__hash__() 从父类继承方法但改变 返回的哈希值的含义__cmp__()__eq__()不再合适的类(例如,通过切换到基于值的相等概念而不是默认的基于身份的相等),可以明确地将自己标记为通过__hash__ = None 在类定义中设置是不可取的.这样做意味着当程序尝试检索其哈希值时,不仅类的实例会引发适当的TypeError,而且在检查时它们也将被正确识别为不可用 isinstance(obj, collections.Hashable) (与定义它们自己的类 __hash__()以显式引发TypeError不同).

  • 仅哈希是不够的,另外你需要覆盖`__eq__`或`__cmp__`. (2认同)
  • @Skurmedel:是的,但是尽管您可以在不重写这些方法的用户类上调用“cmp”并使用“=”,但必须实现其中之一以满足提问者的要求,即具有相似名称和位置的实例具有相同的字典钥匙。 (2认同)

iam*_*dor 15

我注意到在 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}
Run Code Online (Sandbox Code Playgroud)

我假设从某个时候起 Python 就可以自己管理它,但我可能是错的。

  • 我发现这也是真的。 (2认同)