Вы можете использовать функцию, подобную этой, чтобы превратить ваши объекты во что-то хешируемое:
def make_hashable(o):
if isinstance(o, dict):
return frozenset((k, make_hashable(v)) for k, v in o.items())
elif isinstance(o, list):
return tuple(make_hashable(elem) for elem in o)
elif isinstance(o, set):
return frozenset(make_hashable(elem) for elem in o)
else:
return o
Затем вы сохраняете набор видимых объектов и сохраняете только ключи каждого словаря, содержащие объекты, которые вы не видели ранее:
lst = [
{1: {'a':[1,2,3], 'b': 4}},
{2: {'a':[4,5,6], 'd': 5}},
{3: {'a':[1,2,3], 'b': 4}},
]
seen = set()
result_keys = []
for elem in lst:
keep_keys = []
for k, v in elem.items():
v_hashable = make_hashable(v)
if v_hashable not in seen:
seen.add(v_hashable)
keep_keys.append(k)
result_keys.append(keep_keys)
result = [{k: elem[k] for k in keys} for elem, keys in zip(lst, result_keys) if keys]
print(result)
# [{1: {'a': [1, 2, 3], 'b': 4}}, {2: {'a': [4, 5, 6], 'd': 5}}]
Обратите внимание, что, как отмечает blhsing в комментариях, это имеет некоторые ограничения, такие как учет (1, 2)
и [1, 2]
равных, а также {1: 2}
и {(1, 2)}
. Кроме того, некоторые типы не могут быть преобразованы в эквивалентный тип hashable.
РЕДАКТИРОВАТЬ: Как подсказывает a_guest , вы можете обойти неопределенность типа, возвращая сам тип вместе с хешируемым объектом в make_hashable
:
def make_hashable(o):
t = type(o)
if isinstance(o, dict):
o = frozenset((k, make_hashable(v)) for k, v in o.items())
elif isinstance(o, list):
o = tuple(make_hashable(elem) for elem in o)
elif isinstance(o, set):
o = frozenset(make_hashable(elem) for elem in o)
return t, o
Если вам не нужно смотреть на хешируемый объект, это легко обеспечит строгое сравнение типов. Обратите внимание, что в этом случае даже такие вещи, как {1, 2}
и frozenset({1, 2})
будут отличаться.