Для чего используется deepcopy() в Python и как он работает?

«Для чего используется deepcopy() в Python и как он работает?» — вопрос из категории Python, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

deepcopy() из модуля copy используется для создания полной, рекурсивной копии объекта, включая все объекты, на которые он ссылается, объекты внутри них и так далее. Это гарантирует, что между оригиналом и копией не останется общих ссылок на изменяемые (mutable) объекты.

Принцип работы:

  1. Функция deepcopy(x) создает новый объект того же типа, что и x.
  2. Затем она рекурсивно обходит все элементы/атрибуты объекта x.
  3. Для каждого найденного элемента:
    • Если элемент является примитивом (число, строка) или неизменяемым объектом, он просто присваивается в копию (так как его изменение невозможно, общая ссылка безопасна).
    • Если элемент является изменяемым объектом (список, словарь, экземпляр класса), для него рекурсивно вызывается deepcopy(), и в копию помещается уже его глубокая копия.
  4. Для корректной обработки циклических ссылок deepcopy() использует словарь memo, который хранит соответствие id(оригинала) -> копия, чтобы не уйти в бесконечную рекурсию.

Типичные сценарии использования:

  1. Копирование сложных структур данных:
    
    import copy

config = { 'model_params': {'learning_rate': 0.01, 'layers': [64, 32]}, 'data_paths': ['/train.csv', '/test.csv'] }

Создаем экспериментальную копию конфига

new_config = copy.deepcopy(config) new_config['model_params']['layers'].append(16) # Меняем только копию print(config['model_params']['layers']) # [64, 32] - оригинал не тронут


2.  **Работа с изменяемыми аргументами по умолчанию в функциях (избегание известной проблемы):**
```python
def process_data(data, cache=[]):  # ОПАСНО: изменяемый аргумент по умолчанию!
    cache.append(data)
    return cache

# Безопасная версия с deepcopy
from copy import deepcopy
def process_data_safe(data, cache=None):
    if cache is None:
        cache = []
    # Создаем копию кэша, чтобы не менять внешний объект, если он был передан
    current_cache = deepcopy(cache)
    current_cache.append(data)
    return current_cache
  1. Создание независимых копий объектов в симуляциях или играх:
    
    class Unit:
    def __init__(self, health, position):
        self.health = health
        self.position = list(position)  # Изменяемый список

original_unit = Unit(100, [0, 0]) cloned_unit = copy.deepcopy(original_unit) cloned_unit.position[0] = 5 # Движем клон, оригинал остается на месте [0, 0]



**Производительность:** `deepcopy()` может быть медленной операцией для больших и глубоких структур. Всегда оценивайте необходимость её использования. Для простых случаев часто достаточно поверхностного копирования (`copy()`).