Словари Python: метод копирования
Все способы копирования словаря в Python — copy(), dict(), {**d} и deepcopy() — с примерами поведения поверхностной и глубокой копии.
В этой главе рассмотрены все стандартные способы копирования словаря Python, объясняется, почему обычное присваивание (=) не является копированием, и когда вместо поверхностной копии необходима глубокая.
Почему словарь нельзя скопировать с помощью =
Присваивание словаря новой переменной не создаёт копию — оно создаёт вторую ссылку на тот же объект. Любое изменение, сделанное через любую из переменных, затрагивает один и тот же словарь.
original = {'a': 1, 'b': 2}
alias = original # same object, not a copy
alias['c'] = 3
print(original) # {'a': 1, 'b': 2, 'c': 3} — original changed!Чтобы получить по-настоящему независимый словарь, нужно воспользоваться одним из методов копирования, описанных ниже.
Поверхностная копия и глубокая копия
Прежде чем рассматривать методы, полезно понять два вида копирования:
| Вид | Что копируется | Вложенные изменяемые объекты |
|---|---|---|
| Поверхностная копия | Пары ключ-значение верхнего уровня | Ссылки используются совместно — изменение в одном словаре затрагивает другой |
| Глубокая копия | Каждый объект на каждом уровне вложенности | Полностью независима — общих ссылок нет |
Для плоских словарей (значения — строки, числа, None, boolean) поверхностной копии всегда достаточно. Если словарь содержит списки, множества или другие словари в качестве значений, следует рассмотреть глубокую копию.
Метод 1: dict.copy()
Встроенный метод copy() возвращает новый словарь, являющийся поверхностной копией исходного. Это наиболее идиоматичный выбор для плоских словарей.
Синтаксис
new_dict = original_dict.copy()Создание поверхностной копии словаря в Python
Добавление или удаление ключа в new_dict не затронет original_dict, поскольку копия верхнего уровня независима.
Обновление ключей верхнего уровня в скопированном словаре
Метод 2: конструктор dict()
Передача существующего словаря в конструктор dict() создаёт поверхностную копию так же, как copy().
original_dict = {'name': 'Alice', 'age': 30}
new_dict = dict(original_dict)
new_dict['age'] = 31
print(original_dict) # {'name': 'Alice', 'age': 30}
print(new_dict) # {'name': 'Alice', 'age': 31}dict() удобен, когда нужно явно создать словарь из другого отображения или совместить ключевые аргументы с существующим словарём:
defaults = {'color': 'blue', 'size': 'M'}
custom = dict(defaults, size='L', weight='light')
print(custom)
# {'color': 'blue', 'size': 'L', 'weight': 'light'}Метод 3: распаковка словаря {**d}
Оператор распаковки ** объединяет словарь в новый словарный литерал. Для простого копирования он ведёт себя идентично copy(), но также позволяет объединить несколько словарей или переопределить отдельные ключи в одном выражении.
original_dict = {'a': 1, 'b': 2}
new_dict = {**original_dict}
new_dict['c'] = 3
print(original_dict) # {'a': 1, 'b': 2}
print(new_dict) # {'a': 1, 'b': 2, 'c': 3}Копирование с переопределением ключа
config = {'host': 'localhost', 'port': 5432, 'debug': False}
prod_config = {**config, 'host': 'db.example.com', 'debug': False}
print(prod_config)
# {'host': 'db.example.com', 'port': 5432, 'debug': False}Как и copy(), это поверхностная копия — вложенные изменяемые объекты по-прежнему используются совместно.
Подводный камень поверхностной копии: вложенные изменяемые объекты используются совместно
Все три метода выше создают поверхностные копии. Если значение само является изменяемым объектом (например, списком), и оригинал, и копия хранят ссылку на один и тот же внутренний объект.
Обновление изменяемых значений внутри скопированного словаря
Оба словаря отражают изменение, поскольку new_dict['key1'] и original_dict['key1'] указывают на один и тот же объект-список. Это ожидаемое поведение поверхностной копии — это не ошибка.
Метод 4: copy.deepcopy() для полной независимости
Когда необходима полная изоляция — включая вложенные списки, множества и словари — используйте copy.deepcopy() из стандартного модуля copy. Функция рекурсивно копирует каждый объект в структуре.
Глубокая копия в Python
deepcopy() работает с произвольно вложенными структурами и даже с самореферентными объектами. Её недостатки — скорость и использование памяти: она медленнее поверхностной копии, поскольку должна обойти и продублировать весь граф объектов.
Выбор подходящего метода
| Ситуация | Рекомендуемый метод |
|---|---|
| Плоский словарь (без вложенных изменяемых объектов) | dict.copy() или {**d} |
| Объединение / переопределение ключей при копировании | {**d, key: value} или dict(d, key=value) |
| Вложенные изменяемые объекты, нужна полная независимость | copy.deepcopy() |
| Преобразование другого отображения в копию словаря | dict(mapping) |
Резюме
- Оператор
=создаёт псевдоним, а не копию. dict.copy(),dict()и{**d}создают поверхностные копии — ключи верхнего уровня независимы, но вложенные изменяемые объекты используются совместно.copy.deepcopy()создаёт глубокую копию — каждый объект на каждом уровне вложенности дублируется.- Для плоских словарей предпочтительнее использовать
dict.copy()для наглядности. - Используйте
{**d, overrides}, когда нужно скопировать и изменить словарь в одном выражении.
Смотрите также: Методы словарей · Вложенные словари · Перебор словарей