Ответ 1
Во-первых, я думаю, что ваш код работает, а не не элегантен. Там нет непосредственной причины не использовать код, который вы представили.
Есть несколько вещей, которые могут быть лучше, хотя:
Сравнивая тип
Ваш код содержит строку:
if type(dict_foo[field]) == dict:
Это может быть определенно улучшено. Обычно (см. Также PEP8) вы должны использовать isinstance
вместо сравнения типов:
if isinstance(dict_foo[field], dict)
Однако это также вернет True
если dict_foo[field]
является подклассом dict
. Если вы не хотите этого, вы также можете использовать is
вместо ==
. Это будет незначительно (и, вероятно, незаметно) быстрее.
Если вы также хотите разрешить произвольные объекты типа dict, вы можете пойти еще дальше и проверить, является ли он collections.abc.MutableMapping
. Это будет True
для подклассов dict
и dict
и для всех изменяемых отображений, которые явно реализуют этот интерфейс без подкласса dict
, например UserDict
:
>>> from collections import MutableMapping
>>> # from UserDict import UserDict # Python 2.x
>>> from collections import UserDict # Python 3.x - 3.6
>>> # from collections.abc import MutableMapping # Python 3.7+
>>> isinstance(UserDict(), MutableMapping)
True
>>> isinstance(UserDict(), dict)
False
Изменение на месте и возвращаемое значение
Обычно функции либо изменяют структуру данных на месте, либо возвращают новую (измененную) структуру данных. Просто упомянуть несколько примеров: list.append
, dict.clear
, dict.update
все изменяют структуру данных на месте и return None
. Это облегчает отслеживание того, что делает функция. Однако это не жесткое правило, и всегда есть действительные исключения из этого правила. Однако лично я думаю, что подобная функция не должна быть исключением, и я просто удалил бы return dict_del
строку return dict_del
и позволил бы ей неявно возвращать None
, но YMMV.
Удаление ключей из словаря
Вы скопировали словарь, чтобы избежать проблем при удалении пар ключ-значение во время итерации. Однако, как уже упоминалось в другом ответе, вы можете просто перебрать ключи, которые должны быть удалены, и попытаться удалить их:
for key in keys_to_remove:
try:
del dict[key]
except KeyError:
pass
Это дает дополнительное преимущество, заключающееся в том, что вам не нужно вкладывать два цикла (что может быть медленнее, особенно если количество ключей, которые необходимо удалить, очень велико).
Если вам не нравится пустое, except
предложений, вы также можете использовать: contextlib.suppress
(требуется Python 3. 4+):
from contextlib import suppress
for key in keys_to_remove:
with suppress(KeyError):
del dict[key]
Имена переменных
Есть несколько переменных, которые я бы переименовал, потому что они не описательны и даже не вводят в заблуждение:
-
delete_keys_from_dict
вероятно, должен упомянуть обработкуdelete_keys_from_dict_recursive
, возможно,delete_keys_from_dict_recursive
. -
dict_del
звучит как удаленный dict. Я предпочитаю использовать такие имена, какdictionary
илиdct
потому что имя функции уже описывает, что делается со словарем. -
lst_keys
, там же. Я бы, наверное, использовал толькоkeys
там. Если вы хотите быть более конкретным, что-то вродеkeys_sequence
будет иметь больше смысла, потому что оно принимает любуюsequence
(вам просто нужно иметь возможность повторять ее несколько раз), а не только списки. -
dict_foo
, просто нет... -
field
не совсем подходит, это ключ.
Собираем все вместе:
Как я уже говорил, я лично изменю словарь на месте и больше не возвращаю его. Из-за этого я представляю два решения: одно, которое изменяет его на месте, но ничего не возвращает, и другое, которое создает новый словарь с удаленными ключами.
Версия, которая модифицирует на месте (очень похоже на решение Неда Батчелдерса):
from collections import MutableMapping
from contextlib import suppress
def delete_keys_from_dict(dictionary, keys):
for key in keys:
with suppress(KeyError):
del dictionary[key]
for value in dictionary.values():
if isinstance(value, MutableMapping):
delete_keys_from_dict(value, keys)
И решение, которое возвращает новый объект:
from collections import MutableMapping
def delete_keys_from_dict(dictionary, keys):
keys_set = set(keys) # Just an optimization for the "if key in keys" lookup.
modified_dict = {}
for key, value in dictionary.items():
if key not in keys_set:
if isinstance(value, MutableMapping):
modified_dict[key] = delete_keys_from_dict(value, keys_set)
else:
modified_dict[key] = value # or copy.deepcopy(value) if a copy is desired for non-dicts.
return modified_dict
Однако он создает только копии словарей, остальные значения не возвращаются как копии, вы можете легко обернуть их в copy.deepcopy
(я поместил комментарий в соответствующее место кода), если вы этого хотите.