我有子类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 举报,一经查实,本站将立刻删除。