如何检查除某些字段外的2格是否相等?

时间:2018-10-01 11:39:12

标签: python python-3.x dictionary

有两个决定:旧的和更新的。我想检查一下它们是否相等,除了statuslatitudelongitude键之外。

assert old_dict['status'] != updated_dict['status']
assert old_dict['latitude'] != updated_dict['latitude']
assert old_dict['longitude'] != updated_dict['longitude']

for field in ('status', 'latitude', 'longitude'):
    updated_dict.pop(field)
    old_dict.pop(field)

assert old_dict == updated_dict

执行此操作的更Python方式是什么?

7 个答案:

答案 0 :(得分:3)

您可以断言,两个字典的项之间的对称差异是三个键的区别:

assert {k for k, _ in old_dict.items() ^ updated_dict.items()} == {'status', 'latitude', 'longitude'}

答案 1 :(得分:2)

有点不合常规的建议,但是请听我说:

differing = {"status", "latitude", "longitude"}
assert all(
    (old_dict[key] != updated_dict[key]) == (key in differing)
    for key in old_dict
)

对于每个key,我们断言,当且仅当键是不同的键之一时,值才不同。

答案 2 :(得分:1)

您可以通过字典理解过滤两个字典,然后检查是否相等:

def compare_dicts(d1, d2, exc_keys):
    dct1 = {k: v for k, v in d1.items() if k not in exc_keys}
    dct2 = {k: v for k, v in d2.items() if k not in exc_keys}
    return dct1 == dct2

assert compare_dicts(old_dict, updated_dict, {'status', 'latitude', 'longitude'})

答案 3 :(得分:1)

我认为完整测试要求 exception excluded dict键必须不同,并且两个词典可能没有相同的所有键。

一些测试用例可以这样写:

import string
import random

random.seed(0)

keys = list(string.ascii_letters)
excluded = 'r', 'm', 'e'

# the original dict
base_dict = {key: random.randint(1, 100) for key in keys}

# some keys, different from excluded are different
unequal_dict = {key: (val if key not in ('q') else random.randint(1, 100)) for key, val in base_dict.items()}

# only the excluded keys are different
equal_dict = {key: (val if key not in excluded else random.randint(1, 100)) for key, val in base_dict.items()}

# only some of the excluded keys are different
partial_dict = {key: (val if key not in excluded[1:] else random.randint(1, 100)) for key, val in base_dict.items()}

# a copy of the base dict
identical_dict = base_dict.copy()

# one more key is added
not_same_keys_dict = base_dict.copy()
not_same_keys_dict['aa'] = 1

现在old_dict基本上是base_dict,而unequal_dictequal_dictpartial_dictidentical_dictnot_same_keys_dict涵盖了不同的地方极端情况。

然后,我们定义一些辅助函数,以一次测试不同的输入。

def multi_test(func, many_args):
    return [func(*args) for args in many_args]

many_args = (
    (base_dict, unequal_dict, updated),
    (base_dict, equal_dict, updated),
    (base_dict, partial_dict, updated),
    (base_dict, identical_dict, updated),
    (base_dict, not_same_keys_dict, updated))

原始代码的功能如下:

import copy

def dicts_equal_except_orig(dict1, dict2, excluded):
    dict1 = dict1.copy()
    dict2 = dict2.copy()
    result = True
    for key in excluded:
        result = result and (dict1[key] != dict2[key])
        dict1.pop(key)
        dict2.pop(key)
    result = result and (dict1 == dict2)
    return result

print(multi_test(dicts_equal_except_orig, many_args))
# [False, True, False, False, False]

%timeit multi_test(dicts_equal_except_orig, many_args)
# 13.1 µs ± 183 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

在要比较的字典具有一些不共通的键的假设下,这几乎与完成的测试所能达到的速度一样快。 所有其他方法的运行速度虽然要慢得多,但可能会更清洁,在某些情况下,例如,甚至可能更快。当要排除的键数很大时,等等。 另外,如果不需要not_same_key用例,即字典始终具有相同的键,则基于all()的解决方案将更快,因为它们将具有明显的短路,并且它们可以通过以下方式进行转换:

keys = dict1.keys() | dict2.keys()

例如

keys = dict1.keys()

并删除其他if key in dict1 and key in dict2等健全性检查。


为完整起见,我报告了我测试过的所有其他选项:

我自己的带有显式测试的解决方案

def dicts_equal_except(dict1, dict2, excluded):
    keys = dict1.keys() | dict2.keys()
    return all(
        (dict1[key] != dict2[key] if key in excluded else dict1[key] == dict2[key])
        if key in dict1 and key in dict2 else False
        for key in keys)


print(multi_test(dicts_equal_except, many_args))
# [False, True, False, False, False]

%timeit multi_test(dicts_equal_except, many_args)
# 28.3 µs ± 186 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

@blhsing解决方案的功能化

def check_dict_except(dict1, dict2, excluded):
    return {k for k, _ in dict1.items() ^ dict2.items()} == set(excluded)

print(multi_test(check_dict_except, many_args))
# [False, True, False, False, False]

%timeit multi_test(check_dict_except, many_args)
# 30.8 µs ± 498 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

@ L3viathan解决方案的变体

def dicts_equal_all(dict1, dict2, excluded):
    keys = dict1.keys() | dict2.keys()
    return all((dict1[key] == dict2[key]) ^ (key in excluded) for key in keys)

print(multi_test(dicts_equal_all, many_args))
# [False, True, False, False, False]

%timeit multi_test(dicts_equal_all, many_args)
# 29.7 µs ± 316 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

def dicts_equal_all2(dict1, dict2, excluded):
    keys = dict1.keys() | dict2.keys()
    return all((dict1[key] != dict2[key]) == (key in excluded) for key in keys)

print(multi_test(dicts_equal_all2, many_args))
# [False, True, False, False, False]

%timeit multi_test(dicts_equal_all2, many_args)
# 29.9 µs ± 435 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

@jpp答案的改编:

def compare_dicts(dict1, dict2, excluded):
    filter_dict1 = {key: val for key, val in dict1.items() if key not in excluded}
    filter_dict2 = {key: val for key, val in dict2.items() if key not in excluded}
    excluded_dict1 = {key: dict1[key] for key in excluded if key in dict1}
    excluded_dict2 = {key: dict2[key] for key in excluded if key in dict2}
    return filter_dict1 == filter_dict2 and all(dict1[key] != dict2[key] if key in dict1 and key in dict2 else False for key in excluded)

print(multi_test(compare_dicts, many_args))
# [False, True, False, False, False]

%timeit multi_test(compare_dicts, many_args)
# 57.5 µs ± 960 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

答案 4 :(得分:0)

尝试一下:

old_dict.keys() == updated_dict.keys()

如果old_dict键是update_dict键的子集,则为true。

答案 5 :(得分:0)

几行,但是应该很快,因为它不会改变要比较的字典对的结构。

EXC = {"status", "latitude", "longitude"}
saved = {}

for key in EXC:
   saved[key], updated_dict[key] = updated_dict[key], old_dict[key]
cmp = old_dict == updated_dict
for key in EXC:
   old_dict[key], updated_dict[key] = updated_dict[key], saved[key]

答案 6 :(得分:0)

怎么样

ignore = {'status', 'latitude', 'longitude'}
equal = all([old_value == new[key]
             for key, old_value in old.items()
             if key not in ignore])

这一次遍历字典(我认为没有办法解决这个问题)。