微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

python – 比较dict子类的实例

我有子类dict添加一个额外的方法(所以没有覆盖).

现在,我尝试比较其中两个子类,我得到一些奇怪的东西:

>>> d1.items() == d2.items()
True
>>> d1.values() == d2.values()
True
>>> d1.keys() == d2.keys()
True
>>> d1 == d2
False

编辑

那真是太奇怪了……我根本不明白!谁有洞察dict.eq是如何实现的?

以下是所有代码

# ------ Bellow is my dict subclass (with no overriding) :

class ClassSetDict(dict):

    def subsetget(self,klass,default=None):
        class_sets = set(filter(lambda cs: klass <= cs,self))
        # Eliminate supersets
        for cs1 in class_sets.copy():
            for cs2 in class_sets.copy():
                if cs1 <= cs2 and not cs1 is cs2:
                    class_sets.discard(cs2)
        try:
            best_match = list(class_sets)[0]
        except IndexError:
            return default
        return self[best_match]

# ------  Then an implementation of class sets

class ClassSet(object):
    # Set of classes,allowing to easily calculate inclusions
    # with comparison operators : `a < B` <=> "A strictly included in B"

    def __init__(self,klass):
        self.klass = klass

    def __ne__(self,other):
        return not self == other

    def __gt__(self,other):
        other = self._default_to_singleton(other)
        return not self == other and other < self

    def __le__(self,other):
        return self < other or self == other

    def __ge__(self,other):
        return self > other or self == other

    def _default_to_singleton(self,klass):
        if not isinstance(klass,ClassSet):
            return Singleton(klass)
        else:
            return klass


class Singleton(ClassSet):

    def __eq__(self,other):
        other = self._default_to_singleton(other)
        return self.klass == other.klass

    def __lt__(self,other):
        if isinstance(other,AllSubSetsOf):
            return issubclass(self.klass,other.klass)
        else:
            return False


class AllSubSetsOf(ClassSet):

    def __eq__(self,AllSubSetsOf):
            return self.klass == other.klass
        else:
            return False

    def __lt__(self,other.klass) and not other == self
        else:
            return False

# ------ and finally the 2 dicts that don't want to be equal !!!

d1 = ClassSetDict({AllSubSetsOf(object): (int,)})
d2 = ClassSetDict({AllSubSetsOf(object): (int,)})

解决方法

你所看到的问题与子类化dict没有任何关系.事实上,使用常规字典可以看到这种行为.问题是如何定义您正在使用的密钥.一个简单的类,如:

>>> class Foo(object):
...     def __init__(self,value):
...         self.value = value
... 
...     def __eq__(self,other):
...         return self.value == other.value
...

足以证明问题:

>>> f1 = Foo(5)
>>> f2 = Foo(5)
>>> f1 == f2
True
>>> d1 = {f1: 6}
>>> d2 = {f2: 6}
>>> d1.items() == d2.items()
True
>>> d1 == d2
False

缺少的是你忘了定义__hash__.每次更改类的相等语义时,都应确保__hash__方法与它一致:当两个对象相等时,它们必须具有相等的哈希值. dict行为很大程度上取决于键的哈希值.

当你从object继承时,你自动获得__eq__和__hash__,前者比较对象标识,后者返回对象的地址(所以他们同意),但是当你改变__eq__时,你仍然看到旧的__hash__,不再同意和dict迷失了.

只需提供一个__hash__方法,以稳定的方式组合其属性的哈希值.

>>> class Bar(object):
...     def __init__(self,other):
...         return self.value == other.value
... 
...     def __hash__(self):
...         return hash((Bar,self.value))
... 
>>> b1 = Bar(5)
>>> b2 = Bar(5)
>>> {b1: 6} == {b2: 6}
True
>>>

以这种方式使用__hash__时,确保在创建对象后属性不会(或更好,不能)更改也是一个好主意.如果哈希值在dict中收集时发生变化,则密钥将“丢失”,并且可能发生各种奇怪的事情(甚至比您最初询问的问题更奇怪)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐