Словари в Python: объединение и обновление с помощью update и других методов
Обновление словарей методом update и альтернативные подходы
Как обновить словарь данными из другого словаря или набора пар ключ-значение?
Основной способ – метод update(). Он принимает другой словарь, итерируемый объект с парами ключ-значение или именованные аргументы. Все ключи из переданного объекта добавляются или перезаписываются в исходном словаре. Метод изменяет словарь на месте и возвращает None.
# Исходный словарь
user = {'name': 'Анна', 'age': 25}
# Обновление другим словарём
updates = {'age': 26, 'city': 'Москва'}
user.update(updates)
print(user) # {'name': 'Анна', 'age': 26, 'city': 'Москва'}
Python dict update (метод update словарей в python)
{'name': 'Анна', 'age': 26, 'city': 'Москва'}
Типичные ошибки:
- Передача не словаря и не итерируемого объекта с парами - TypeError.
- Попытка обновить ключами нестрокового типа при использовании именованных аргументов - TypeError (именованные аргументы преобразуются только в строки).
Как объединить два словаря в новый, не изменяя исходные?
Начиная с Python 3.9 можно использовать оператор | (объединение). Он возвращает новый словарь, содержащий все пары из левого и правого операндов. При совпадении ключей значение берётся из правого словаря.
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
merged = dict1 | dict2
print(merged) # {'a': 1, 'b': 3, 'c': 4}
print(dict1) # {'a': 1, 'b': 2} - исходный не изменился
{'a': 1, 'b': 3, 'c': 4}
{'a': 1, 'b': 2}
Ошибки возможны только если операнды не являются словарями - TypeError.
Как создать новый словарь из нескольких, используя распаковку?
Оператор ** (распаковка) позволяет собрать ключи и значения из нескольких словарей в один литерал. Порядок важен: последнее упоминание ключа перезаписывает предыдущие. Работает во всех версиях Python 3.5+.
base = {'x': 10, 'y': 20}
add = {'y': 30, 'z': 40}
new = {**base, **add}
print(new) # {'x': 10, 'y': 30, 'z': 40}
{'x': 10, 'y': 30, 'z': 40}
Ограничения: ключи должны быть строками (или неизменяемыми хешируемыми типами). При попытке распаковать не словарь - TypeError.
Как обновить словарь с дополнительной логикой (например, суммирование значений)?
Применяется цикл for с индивидуальной обработкой каждого ключа. Это даёт полный контроль над тем, как поступать с новыми и существующими записями.
inv = {'apples': 5, 'oranges': 3}
shipment = {'apples': 2, 'bananas': 7}
for key, value in shipment.items():
inv[key] = inv.get(key, 0) + value
print(inv) # {'apples': 7, 'oranges': 3, 'bananas': 7}
{'apples': 7, 'oranges': 3, 'bananas': 7}
Сложность в том, что для каждого случая логику приходится писать вручную, что может привести к ошибкам при неверном использовании get() или пропуске условий.
Как добавить отсутствующие ключи без перезаписи существующих?
Метод setdefault(key, default) вставляет ключ со значением default, только если его ещё нет. Полезно для инициализации значений по умолчанию перед обновлением.
d = {'a': 1}
d.setdefault('a', 100) # ключ 'a' уже есть, ничего не меняется
d.setdefault('b', 200) # добавляется 'b': 200
print(d) # {'a': 1, 'b': 200}
{'a': 1, 'b': 200}
Метод возвращает значение по ключу (существующее или только что установленное), что иногда сбивает с толку новичков, ожидающих None.
Как создать цепочку словарей для поиска без изменения исходных?
Класс collections.ChainMap объединяет несколько словарей в единую логическую структуру. Поиск происходит по всем словарям по порядку, но обновления затрагивают только первый словарь.
from collections import ChainMap
defaults = {'theme': 'light', 'lang': 'en'}
user_settings = {'lang': 'fr', 'font': 'Arial'}
config = ChainMap(user_settings, defaults)
print(config['theme']) # 'light' - из defaults
print(config['lang']) # 'fr' - из user_settings
config['lang'] = 'de' # меняется только user_settings
light fr
Ошибки возникают при попытке удалить ключ, которого нет в первом словаре (KeyError). Также ChainMap не поддерживает метод update() в привычном виде (обновляет только первый словарь).
Как обновить словарь с помощью итерации по парам ключ-значение из списка кортежей?
Метод update() может принимать итерируемый объект, каждый элемент которого является кортежем (или итерируемым из двух элементов). Это удобно, когда данные поступают, например, из CSV или базы данных.
pairs = [('a', 10), ('b', 20)]
d = {}
d.update(pairs)
print(d) # {'a': 10, 'b': 20}
{'a': 10, 'b': 20}
Если в списке встретится кортеж длиной не 2 - возникнет ValueError.
Расширенные примеры работы с обновлением словарей
Пример 1. Обновление вложенных словарей (поверхностное копирование)
original = {'user': {'name': 'Иван', 'age': 30}, 'version': 1}
update_data = {'user': {'city': 'СПб'}}
original.update(update_data)
print(original)
# Результат: {'user': {'city': 'СПб'}, 'version': 1} - внутренний словарь заменился целиком
{'user': {'city': 'СПб'}, 'version': 1}
Обратите внимание: update не выполняет глубокое слияние, а заменяет значение целиком. Для вложенного обновления требуется дополнительная логика.
Пример 2. Использование update с именованными аргументами
settings = {'width': 800}
settings.update(height=600, width=1024)
print(settings) # {'width': 1024, 'height': 600}
{'width': 1024, 'height': 600}
Имена аргументов становятся строковыми ключами. Это удобно для передачи небольшого количества параметров, но не подходит для ключей с пробелами или специальными символами.
Пример 3. Объединение словарей с помощью оператора |= (Python 3.9+)
base = {'x': 0, 'y': 0}
extra = {'y': 5, 'z': 1}
base |= extra
print(base) # {'x': 0, 'y': 5, 'z': 1}
{'x': 0, 'y': 5, 'z': 1}
Оператор |= изменяет левый словарь на месте, в отличие от |, который создает новый.
Пример 4. Создание словаря из списка ключей и значений с помощью dict comprehension и update
keys = ['a', 'b', 'c']
values = [1, 2, 3]
result = {}
result.update(zip(keys, values))
print(result) # {'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2, 'c': 3}
Этот способ работает, когда количество ключей и значений совпадает. В противном случае zip обрезает по меньшему списку.
Пример 5. Обновление словаря с игнорированием некоторых ключей (фильтрация)
def safe_update(dest, src, skip_keys):
for key, value in src.items():
if key not in skip_keys:
dest[key] = value
d = {'a': 1, 'b': 2}
s = {'b': 3, 'c': 4, 'd': 5}
safe_update(d, s, {'c'})
print(d) # {'a': 1, 'b': 3, 'd': 5} - ключ 'c' пропущен
{'a': 1, 'b': 3, 'd': 5}
Такой подход позволяет точечно контролировать, какие ключи могут быть изменены или добавлены.
Пример 6. Объединение двух словарей с суммированием числовых значений
from collections import Counter
a = {'apple': 3, 'banana': 2}
b = {'apple': 1, 'orange': 4}
c = dict(Counter(a) + Counter(b))
print(c) # {'apple': 4, 'banana': 2, 'orange': 4}
{'apple': 4, 'banana': 2, 'orange': 4}
Использование Counter удобно, когда все значения числовые и нужно сложить их, а не перезаписать.
Пример 7. Глубокое слияние вложенных словарей с помощью рекурсии
def deep_merge(base, override):
result = base.copy()
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
d1 = {'a': 1, 'b': {'c': 2, 'd': 3}}
d2 = {'b': {'d': 4, 'e': 5}, 'f': 6}
merged = deep_merge(d1, d2)
print(merged) # {'a': 1, 'b': {'c': 2, 'd': 4, 'e': 5}, 'f': 6}
{'a': 1, 'b': {'c': 2, 'd': 4, 'e': 5}, 'f': 6}
Этот рекурсивный подход позволяет объединять многоуровневые структуры, сохраняя все ветви.