Python 有效地比较多个字典和异常

Python 有效地比较多个字典和异常,python,Python,我想比较多个字典,确保所有键和值都相同,但有一些例外。我已经有了一个比较它们的函数,但是它花费的时间太长,我的脚本永远不会结束 在上下文中,我想浏览一个文件列表,并合并所有相同的词典。当前我的循环如下所示: out_files = list(itertools.chain(*unpaired_files)) for items in itertools.product(*new_files): if items and self._compare_multiple

我想比较多个字典,确保所有键和值都相同,但有一些例外。我已经有了一个比较它们的函数,但是它花费的时间太长,我的脚本永远不会结束

在上下文中,我想浏览一个文件列表,并合并所有相同的词典。当前我的循环如下所示:

    out_files = list(itertools.chain(*unpaired_files))
    for items in itertools.product(*new_files):
        if items and self._compare_multiple_dicts_except(all_exceptions, *items):
这是比较字典的功能:

def _compare_multiple_dicts_except(exceptions, *dicts):
    keys = []
    for d in dicts:
        keys.append(
            sorted([key for key in d.keys() if key not in exceptions]))
    if not all(x == keys[0] for x in keys):
        return False
    for key in keys[0]:
        items = [d[key] for d in dicts]
        if not all(x == items[0] for x in items):
            return False
    return True
一些示例用法:

>>> _compare_multiple_dicts_except(['a', 'b'], {'a': 1, 'c': 3, 'd': 4}, {'a': 2, 'c': 3, 'd': 4})
True
>>> _compare_multiple_dicts_except(['a', 'b'], {'a': 1, 'c': 3, 'd': [4, 5]}, {'c': 4, 'd': [4, 5]})
False
我不知道我是否可以做些什么来优化它,或者我是否需要完全重新考虑如何合并词典


还必须注意的是,并非所有内容都是可散列的,因为我有一些numpy数组和列表,因此,我尝试过的所有设置示例都不起作用。

异常设置为一个集合而不是一个列表,这样每个成员资格测试都可以在固定时间而不是线性时间内完成,从而提高代码的性能

您还可以将每个dict转换为一组项元组,以便对它们执行对称差异,以确保其差异的键是
异常集的子集

def compare_multiple_dicts_except(exceptions, *dicts):
    first, *rest = dicts
    reference = set(first.items())
    return all({k for k, _ in reference.symmetric_difference(d.items())} <= exceptions for d in rest)
返回:
True

而且:

compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': 4}, {'c': 4, 'd': 4})
compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': [4, 5]}, {'c': 4, 'd': [4, 5]})
返回:
False

如果dict的值包含更多的子dict或子列表,如编辑所示,则可以使用递归函数将每个子dict转换为冻结的项集,并将每个子列表转换为冻结的集合。计数器
对象:

from collections import Counter

def freeze(o):
    if isinstance(o, list):
        return frozenset(Counter(map(freeze, o)).items())
    if isinstance(o, dict):
        return frozenset((k, freeze(v)) for k, v in o.items())
    return o

def compare_multiple_dicts_except(exceptions, *dicts):
    first, *dicts = dicts
    reference = freeze(first)
    return all({k for k, _ in reference.symmetric_difference(freeze(d))} <= exceptions for d in dicts)

返回:
False

一种更有效的方法可能是在进行比较之前,在不使用
异常中的键的情况下简单地重建DICT:

def compare_multiple_dicts_except(exceptions, *dicts):
    first, *rest = ({k: d[k] for k in d.keys() - exceptions} for d in dicts)
    return all(first == d for d in rest)
以便:

compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': 4}, {'a': 2, 'c': 3, 'd': 4})
compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': [4, 5]}, {'c': 4, 'd': [4, 5]})
compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': 4}, {'a': 2, 'c': 3, 'd': 4})
返回:
True

而且:

compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': 4}, {'c': 4, 'd': 4})
compare_multiple_dicts_except({'a', 'b'}, {'a': 1, 'c': 3, 'd': [4, 5]}, {'c': 4, 'd': [4, 5]})

返回:
False

因此我意识到,对于我的大多数情况,字典之间的键是不同的,因此我对我的代码和blhsing的答案的变化进行了一些计时测试,并获得了以下结果

我选择的三种变体是我的原始代码:

# Number 1
def _compare_multiple_dicts_except(exceptions, *dicts):
    keys = []
    for d in dicts:
        keys.append(
            sorted([key for key in d.keys() if key not in exceptions]))
    if not all(x == keys[0] for x in keys):
        return False
    for key in keys[0]:
        items = [d[key] for d in dicts]
        if not all(x == items[0] for x in items):
            return False
    return True
blhsing关于对称差分的回答,以及我首先比较键的代码:

# Number 2
def _compare_multiple_dicts_except2(exceptions, *dicts):
    first_keys = dicts[0].keys() - exceptions
    for d in dicts[1:]:
        if d.keys() - exceptions != first_keys:
            return False
    first, *dicts = dicts
    reference = freeze(first)
    return all({k for k, _ in reference.symmetric_difference(freeze(d))} <= exceptions for d in dicts)
在我尝试的所有变化中,我发现先比较关键点和集减法是最有效的

不同的键 我使用具有不同键的字典测试了各种解决方案,并在10000000次迭代中计时。我创建了字典来比较:

a = dict.fromkeys("sadjalsdjalskvd")
b = dict.fromkeys("hlkdflkjasdvdae")
变体1在我的机器上花费了44秒。变异2在我的机器上花了20秒。变体3在我的机器上也用了20秒。这是因为2和3使用相同的代码来比较键。然而,很明显,变化1较慢

获胜者:变体2和变体3

同样的钥匙 接下来,我比较了它们具有相同的键,但有一个值发生了更改。为此,我使用了以下词典,并针对我的所有变体运行了它。我仍然运行了10000000次迭代

a = dict.fromkeys("sadjalsdjalskvd")
a2 = a.copy()
a2['s'] = 1
变体1在我的机器上花费了99秒。变异2花了130秒。最后,变体3用了61秒

获胜者:变体3

总体上
总的来说,我将在我的代码中使用变体3,但我愿意对我的代码进行更多的优化。

我从第二个测试用例中得到了答案。问题是否在第一个代码块中?您缺少输入和导入。请给我们一个样本,就像您在上一个“示例用法”模块中所做的一样。或者这个问题只是为了修正,除了第二种情况,我得到了不同的结果。我改为删除self,因为它最初在一个类中,两个样本都适合我。这很理想,我已经看到了一些关于使用对称差分的答案,但问题是,我的字典包含一些不可散列的列表。我将编辑我的示例以使其更清楚。