未调用Python\uu eq\uu方法

2024-10-01 09:19:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一组对象,并且对从中获取特定对象感兴趣。经过一番研究,我决定使用这里提供的解决方案:http://code.activestate.com/recipes/499299/

问题是它似乎不起作用。在

我有两个类是这样定义的:

class Foo(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    def __key(self):
        return (self.a, self.b, self.c)
    def __eq__(self, other):
        return self.__key() == other.__key()
    def __hash__(self):
        return hash(self.__key())

class Bar(Foo):
    def __init__(self, a, b, c, d, e):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e

注意:这两个类的相等只能在属性a,b,c上定义

http://code.activestate.com/recipes/499299/中的包装器_CaptureEq也定义了它自己的__eq__方法。问题是这个方法永远不会被调用(我想)。考虑一下

^{pr2}$

bar_equiv.d应该等于4,同样地,bar_equiv .e应该等于5,但是它们不是。就像我提到的,在执行bar_2 in summary语句时,__CaptureEq__eq__方法没有被调用。在

为什么没有调用__CaptureEq__eq__方法?希望这不是一个太模糊的问题。在


Tags: 对象方法keyselfcomhttpreturn定义
3条回答

问题是set比较两个对象的“错误方式”,以便此模式拦截对__eq__()的调用。2006年的配方显然是针对容器编写的,当被问到是否存在x时,会检查容器中已经存在的候选y值:

x == y

比较,在这种情况下,x上的__eq__()可以在搜索期间执行特殊操作。但是set对象的比较方式与此相反:

^{pr2}$

对于集合中的每个y。因此,当您的数据类型是set时,此模式在这种形式中可能根本不可用。您可以通过如下方式插入Foo.__eq__()来确认这一点:

def __eq__(self, other):
    print '__eq__: I am', self.d, self.e, 'and he is', other.d, other.e
    return self.__key() == other.__key()

然后您将看到一条消息,如:

__eq__: I am 4 5 and he is 10 11

确认相等性比较对集合中已经存在的对象提出了相等性问题,这是,唉,不是用Hettinger的_CaptureEq对象包装的对象。在

更新:

我忘了给你一个建议:你想过用字典吗?因为这里有一个密钥的概念,它是对象内部数据的子集,所以您可能会发现,将密钥的概念从对象本身的概念中分离出来,可能会减少尝试这种复杂的对象拦截的需要。只需编写一个新函数,在给定对象和字典的情况下,计算键并在字典中查找,如果存在键,则返回已在字典中的对象,否则在键处插入新对象。在

更新2:好吧,看看这个-尼克的答案在一个方向使用NotImplemented,在另一个方向使用NotImplemented来进行比较。给那家伙几个+1!在

布兰登的回答很有启发性,但不正确。实际上有两个问题,一个是 依赖于_CaptureEq作为一个旧样式的类编写的方法(因此,如果您在python3上使用基于散列的容器来尝试它,它将无法正常工作),并且有一个具有您自己的Foo.__eq__定义的配方,当它应该说“我不知道,询问另一个对象是否相等”时,它明确声明两个对象不相等。在

配方问题很容易解决:只需在比较包装器类上定义__hash__

class _CaptureEq:
    'Object wrapper that remembers "other" for successful equality tests.'
    def __init__(self, obj):
        self.obj = obj
        self.match = obj
    # If running on Python 3, this will be a new-style class, and
    # new-style classes must delegate hash explicitly in order to populate
    # the underlying special method slot correctly.
    # On Python 2, it will be an old-style class, so the explicit delegation
    # isn't needed (__getattr__ will cover it), but it also won't do any harm.
    def __hash__(self):
        return hash(self.obj)
    def __eq__(self, other):
        result = (self.obj == other)
        if result:
            self.match = other
        return result
    def __getattr__(self, name):  # support anything else needed by __contains__
        return getattr(self.obj, name)

您自己的__eq__定义的问题也很容易解决:在适当的时候返回NotImplemented,这样您就不会声称要为与未知对象的比较提供一个明确的答案:

^{pr2}$

通过这两个修复,您将发现Raymond的get_equivalent配方完全可以正常工作:

>>> from capture_eq import *
>>> bar_1 = Bar(1,2,3,4,5)
>>> bar_2 = Bar(1,2,3,10,11)
>>> summary = set((bar_1,))
>>> assert(bar_1 == bar_2)
>>> bar_equiv = get_equivalent(summary, bar_2)
>>> bar_equiv.d
4
>>> bar_equiv.e
5

更新:澄清了显式的__hash__重写只是为了正确处理python3的情况。在

这里有两个问题。首先是:

t = _CaptureEq(item)
if t in container:
    return t.match
return default

不会做你想做的事。特别是,t将永远不会出现在container中,因为_CaptureEq没有定义{}。这在Python3中变得更加明显,因为它将向您指出这一点,而不是提供默认的__hash___CaptureEq的代码似乎认为,提供一个__getattr__可以解决这个问题——它不会,因为Python的特殊方法查找不一定会像普通的属性查找一样经过所有相同的步骤——这就是为什么__hash__(以及其他各种方法)需要在类上定义,并且不能在实例上进行monkeypatch。因此,最直接的方法是定义_CaptureEq.__hash__,如下所示:

^{pr2}$

但这仍然不能保证有效,因为第二个问题:set查找不能保证测试相等性。set是基于哈希表的,只有在哈希桶中有多个项时才进行相等性测试。您不能(也不想)强制将哈希不同的项放入同一个bucket中,因为这是set的全部实现细节。首先,最简单的方法是列出:

summary = [bar_1]
assert(bar_1 == bar_2)
bar_equiv = get_equivalent(summary, bar_2)
assert(bar_equiv is bar_1)

相关问题 更多 >