__hash__、__eq__ 和 "in" 运算符

1 投票
1 回答
651 浏览
提问于 2025-04-18 09:28

我看到很多关于 __eq____hash__ 以及它们之间关系的问题,虽然我遇到了一个让我感到困惑的现象,但我想先澄清一下。

我有一个字典 team,里面的键是很多 player 的实例。这个 player 类比较复杂,但它包含了 __hash____eq__ 这两个方法。我还想提到的是,player 类里有一些属性是字典,我觉得这可能是问题的根源。

无论如何,我的代码如下:

if player in team.keys():
    name, height = team[player]

奇怪的是,当我检查 player in team 时,它返回 True(这意味着条件成立,所以下面的代码会执行),但第二行却抛出了 KeyError。我搞不懂为什么 in 能正常工作,而键却没有正确地被哈希。

有什么建议吗?

编辑:

player 类包含以下魔法方法。cnfg_dict 是一个字典,里面有几个用户指定的配置,其中 required_attrs 用来区分不同的 player 实例(据说是这样...)。

def __init__(self, cnfg_dict):
    self.config_dict = cnfg_dict
    self.required_attrs = ['attr1', 'attr2']

def __eq__(self, other):
    return all([self.config_dict[attr] == other.config_dict[attr] for attr in    self.required_attrs])

def __ne__(self, other):
    required_attrs = ['dt', 'author']
    return any([self.config_dict[attr] != other.config_dict[attr] for attr in required_attrs])

def __repr__(self):
    return str([str(a)+'='+str(self.config_dict[a]) for a in self.required_attrs])

def __hash__(self):
    return hash(repr(self))

1 个回答

0

我对你说的“相等的对象会有相同的 repr 函数”这个观点不太放心。特别是当你处理数字的时候,这样做是有风险的。这里有个例子:

 [in] >>> a = {'name': 'Zelaznik', 'height': 6}
 [in] >>> b = {'name': 'Zelaznik', 'height': 6.0}
 [in] >>> a == b
[out] >>> True
 [in] >>> repr(a['height']) == repr(b['height'])
[out] >>> False

我猜你可能在处理一个玩家的身高时遇到了这个问题。

这里有个简单的方法来解决 hash 函数的问题:

def __hash__(self):
    dct = self.confg_dict
    keys = self.required_attr
    return hash(tuple([dct[k] for k in keys]))

看起来你在使用一个叫“Player”的类,这个类有一组固定的键。这样的话,你可以通过使用 namedtuple 来简化你的工作。

from collections import namedtuple
Player = namedtuple('Player', ('name', 'height', 'opt_attr1', 'opt_attr2'))
Player.__new__.__defaults__ = (None, None) #Sets default values for last two arguments.

https://docs.python.org/2/library/collections.html#collections.namedtuple

撰写回答