我会假设完整的测试需要
异常 或
排除
字典键必须不同,并且这两个字典可能没有所有相同的键。
一些测试用例可以编写如下:
import string
import random
random.seed(0)
keys = list(string.ascii_letters)
excluded = 'r', 'm', 'e'
base_dict = {key: random.randint(1, 100) for key in keys}
unequal_dict = {key: (val if key not in ('q') else random.randint(1, 100)) for key, val in base_dict.items()}
equal_dict = {key: (val if key not in excluded else random.randint(1, 100)) for key, val in base_dict.items()}
partial_dict = {key: (val if key not in excluded[1:] else random.randint(1, 100)) for key, val in base_dict.items()}
identical_dict = base_dict.copy()
not_same_keys_dict = base_dict.copy()
not_same_keys_dict['aa'] = 1
现在,old_dict
基本上是base_dict
,而unequal_dict
、equal_dict
、partial_dict
、identical_dict
和not_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))
%timeit multi_test(dicts_equal_except_orig, many_args)
这是在假设要比较的字典有一些不共同键的情况下,生成测试中可以获得的最快速度。
所有其他方法都明显较慢,尽管可能更加清晰,并且在某些情况下甚至可能更快,例如当需要排除的键数很大时等等。
此外,如果不需要
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))
%timeit multi_test(dicts_equal_except, many_args)
一个对@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))
%timeit multi_test(check_dict_except, many_args)
@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))
%timeit multi_test(dicts_equal_all, many_args)
并且
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))
%timeit multi_test(dicts_equal_all2, many_args)
一种对@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))
%timeit multi_test(compare_dicts, many_args)