在循环定义的字典上使用`==`运算符

时间:2015-07-14 19:40:44

标签: python python-3.x dictionary circular-reference

Python允许将词典与==

进行比较
import copy

child = {'name': 'child'}
parent_1 = {'name': 'parent', 'child': child}
parent_2 = copy.deepcopy(parent_1)
print(parent_1 == parent_2)

按照您的预期打印True

Python还允许字典循环引用。

child = {'name': 'child'}
parent_1 = {'name': 'parent', 'child': child}
child['parent'] = parent_1  # Create the circular reference

但是,尝试在带有循环引用的字典上使用==运算符会引发错误。

parent_2 = copy.deepcopy(parent_1)
print(parent_1 == parent_2)

返回

C:\Python34\python.exe -i C:/Users/anon/.PyCharm40/config/scratches/scratch_5
Traceback (most recent call last):
  File "C:/Users/anon/.PyCharm40/config/scratches/scratch_5", line 11, in <module>
    print(parent_1 == parent_2)
RuntimeError: maximum recursion depth exceeded in comparison

如何检查两个带有循环引用的字典是否相等?

4 个答案:

答案 0 :(得分:3)

你需要平等地定义你的意思。通常&#34;相等&#34;对于字典意味着所有的键/值对都是&#34;等于&#34;&#39;。如果字典具有对自身的引用,则此equ等定义可能会导致递归定义,即a == b iff a == b

举个简单的例子:

a = {}; a['item'] = a
b = {}; b['item'] = b

ab是否相等?要了解这一点,您需要先了解ab是否相等......

您可以创建一个特殊的equal,如下所示:

def equal(a, b, special=[]):
    if not isinstance(a, dict) or not isinstance(b, dict):
        return a == b

    special = special + [a, b]
    set_keys = set(a.keys())
    if set_keys != set(b.keys()):
        return False

    for key in set_keys:
        if any(a[key] is i for i in special):
            continue
        elif any(b[key] is i for i in special):
            continue
        elif not equal(a[key], b[key], special):
            return False
    return True

答案 1 :(得分:0)

import copy

child = {'name': 'child'}
parent_1 = {'name': 'parent', 'child': child}
child['parent'] = parent_1  # Create the circular reference

parent_2 = copy.copy(parent_1)
print(parent_1 == parent_2)

如果您使用copy.copy代替copy.deepcopy,则会运行时没有错误。

  

浅层和深层复制之间的区别仅与之相关   复合对象(包含其他对象的对象,如列表或   类实例):

     

•浅拷贝构造一个新的复合对象然后(到   尽可能地将引用插入到找到的对象中   原来。

     

•深层复制构造一个新的复合对象,然后递归地,   将副本插入原始文件中找到的对象。

https://docs.python.org/2/library/copy.html

这可能是存储问题,使用deepcopy()强制实际递归,而copy()只能检查引用

答案 2 :(得分:0)

你需要编写自己的比较过程,它需要四个参数,两个要比较的东西,两个字典/列表堆栈(每个被比较的东西一个)。

如果要比较的任何东西都包含在相应的堆栈中,如果它们在同一位置的自己的堆栈中则返回true,否则返回false。

否则,如果它们是字典,请确保它们具有相同的键集(否则返回false),并对每个值进行递归,将字典添加到两个堆栈中。

如果它们是列表,请确保它们的长度相同(或返回false)并递归每对成员,将列表添加到两个堆栈中。

要开始工作,请使用要比较的内容和两个空堆栈调用递归过程。您可以将此包装在另一个只带两个参数的过程中。

def my_compare(a, b):
    return my_compare_helper(a, b, [], [])

def my_index(thing, stack):
    for i in range(len(stack)):
        if thing is stack[i]:
            return i
    return -1

def my_compare_helper(a, b, a_stack, b_stack):
    a_loc = my_index(a, a_stack)
    b_loc = my_index(b, b_stack)
    if a_loc != -1 or b_loc != -1:
        return a_loc == b_loc

    a_stack = [a] + a_stack
    b_stack = [b] + b_stack

    if isinstance(a, list):
        if not isinstance(b, list) or len(a) != len(b):
            return False
        for a_thing, b_thing in zip(a, b):
            if not my_compare_helper(a_thing, b_thing, a_stack, b_stack):
                return False
        return True

    if isinstance(a, dict):
        if not isinstance(b, dict):
            return False
        a_keys = sorted(a.keys())
        b_keys = sorted(b.keys())
        if a_keys != b_keys:    # Keys can't be recursive.
            return False
        for key in a_keys:
            if not my_compare_helper(a[key], b[key], a_stack, b_stack):
                return False
        return True

    return a == b

样本用法:

>>> a = [1, 2, {}]
>>> a[1] = a
>>> a[2]['x'] = a
>>> b = [1, 2, {}]
>>> b[1] = b
>>> b[2]['x'] = b
>>> my_compare(a, b)
True
>>> 

答案 3 :(得分:0)

当我做其他事情时,我发现了pickle模块处理递归字典等。考虑到这一点,这可能对你有用:

import copy
import cPickle

a = {}
a['self'] = a
a['list'] = [a, a, 0]

b = copy.deepcopy(a)
print(cPickle.dumps(a) == cPickle.dumps(b))
# True

b['self'] = copy.deepcopy(b)
print(cPickle.dumps(a) == cPickle.dumps(b))
# False