Я бы предположил, что полный тест требует, чтобы исключение или excluded
ключи ввода были разными, и что в двух словарях могут быть не все общие ключи.
Некоторые тестовые случаиможно написать так:
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_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))
# [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)