← Назад к вопросам

Как сделать полную копию словаря?

1.0 Junior🔥 121 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как сделать полную копию словаря в Python?

Это кажется простой задачей, но копирование объектов в Python имеет подводные камни. Давайте разберёмся во всех способах и их различиях.

1. Поверхностная копия: dict.copy()

Самый простой и быстрый способ, но копирует только первый уровень:

# Создаём словарь
original = {
    'name': 'John',
    'age': 30,
    'hobbies': ['reading', 'gaming']
}

# Поверхностная копия
shallow = original.copy()

# Изменяем простое значение
shallow['name'] = 'Jane'
print(original['name'])  # 'John' (изменился только shallow)

# Но списки остаются ссылками!
shallow['hobbies'].append('cooking')
print(original['hobbies'])  # ['reading', 'gaming', 'cooking']
# Изменился и original!

Визуально:

original ─── name: 'John'
           ├─ age: 30
           └─ hobbies ──> ['reading', 'gaming']
                              ↑
shallow ──── name: 'Jane'     |
          ├─ age: 30          |
          └─ hobbies ─────────┘

2. Глубокая копия: copy.deepcopy()

Полная копия всех уровней вложенности:

import copy

original = {
    'name': 'John',
    'age': 30,
    'hobbies': ['reading', 'gaming'],
    'address': {
        'city': 'New York',
        'zip': '10001'
    }
}

# Глубокая копия
deep = copy.deepcopy(original)

# Изменяем на любом уровне
deep['name'] = 'Jane'
deep['hobbies'].append('cooking')
deep['address']['city'] = 'Los Angeles'

print(original['name'])            # 'John' ✓
print(original['hobbies'])         # ['reading', 'gaming'] ✓
print(original['address']['city']) # 'New York' ✓
# Всё осталось как было!

Визуально:

original ─── name: 'John'
           ├─ hobbies ──> ['reading', 'gaming']
           └─ address ──> {'city': 'New York'}

deep ────── name: 'Jane' (копия)
         ├─ hobbies ──> ['reading', 'gaming', 'cooking'] (новый список)
         └─ address ──> {'city': 'Los Angeles'} (новый словарь)

3. Сравнение методов

import copy
import time

# Создаём большой словарь
big_dict = {
    f'key_{i}': {
        'value': i,
        'list': list(range(10))
    }
    for i in range(1000)
}

# Метод 1: dict.copy()
start = time.time()
for _ in range(1000):
    s = big_dict.copy()
print(f'dict.copy(): {time.time() - start:.4f}s')  # ~0.01s

# Метод 2: copy.deepcopy()
start = time.time()
for _ in range(1000):
    d = copy.deepcopy(big_dict)
print(f'deepcopy(): {time.time() - start:.4f}s')   # ~0.5s (медленнее)

# dict.copy() быстрее в 50x раз!

4. Альтернативные способы копирования

Способ 1: Распаковка dict (поверхностная)

original = {'a': 1, 'b': [2, 3]}
copy1 = {**original}  # dict.copy() эквивалент
copy1['a'] = 10
print(original['a'])  # 1 (только первый уровень)

Способ 2: dict() конструктор (поверхностная)

original = {'a': 1, 'b': [2, 3]}
copy2 = dict(original)
copy2['a'] = 10
print(original['a'])  # 1

Способ 3: JSON сериализация (глубокая, но ограниченная)

import json

original = {
    'name': 'John',
    'age': 30,
    'hobbies': ['reading', 'gaming']
}

# Преобразуем в JSON и обратно
copy3 = json.loads(json.dumps(original))
copy3['hobbies'].append('cooking')
print(original['hobbies'])  # ['reading', 'gaming']

# Но это не работает для всех типов!
original_with_date = {
    'created': datetime.now()  # datetime не сериализуется в JSON
}
json.dumps(original_with_date)  # TypeError!

5. Практический пример: когда использовать что

import copy

class User:
    def __init__(self, name: str, tags: list):
        self.name = name
        self.tags = tags

# НЕПРАВИЛЬНО: забыли про ссылки
def add_tag_wrong(user: User, tag: str) -> User:
    user_copy = user.copy()  # Ошибка!
    user_copy.tags.append(tag)
    return user_copy

# ПРАВИЛЬНО: используем глубокую копию
def add_tag_correct(user: User, tag: str) -> User:
    user_copy = copy.deepcopy(user)
    user_copy.tags.append(tag)
    return user_copy

user = User('John', ['admin'])
user_copy = add_tag_correct(user, 'moderator')
print(user.tags)       # ['admin']
print(user_copy.tags)  # ['admin', 'moderator']

6. Копирование с фильтрацией

original = {
    'username': 'john',
    'password': 'secret123',
    'email': 'john@example.com'
}

# Копируем, но исключаем чувствительные данные
safe_copy = {
    key: value
    for key, value in original.items()
    if key != 'password'
}

print(safe_copy)  # {'username': 'john', 'email': '...'}

7. Копирование вложенных словарей

import copy
from typing import Any, Dict

def smart_copy(data: Dict[str, Any], deep: bool = True) -> Dict[str, Any]:
    """Умное копирование с опциями"""
    if deep:
        return copy.deepcopy(data)
    else:
        return data.copy()

original = {
    'level1': {
        'level2': {
            'level3': [1, 2, 3]
        }
    }
}

# Поверхностная
shallow = smart_copy(original, deep=False)
shallow['level1']['level2']['level3'].append(4)
print(original['level1']['level2']['level3'])  # [1, 2, 3, 4]

# Глубокая
deep = smart_copy(original, deep=True)
deep['level1']['level2']['level3'].append(5)
print(original['level1']['level2']['level3'])  # [1, 2, 3, 4] (без 5!)

8. Копирование с пользовательскими объектами

import copy

class Address:
    def __init__(self, city: str, zip_code: str):
        self.city = city
        self.zip_code = zip_code

class Person:
    def __init__(self, name: str, address: Address):
        self.name = name
        self.address = address
    
    def __copy__(self):
        """Поверхностная копия"""
        return Person(self.name, self.address)
    
    def __deepcopy__(self, memo):
        """Глубокая копия"""
        return Person(
            copy.deepcopy(self.name, memo),
            copy.deepcopy(self.address, memo)
        )

addr = Address('NYC', '10001')
person = Person('John', addr)

# Используем встроенные методы copy
person_copy = copy.deepcopy(person)
person_copy.address.city = 'LA'

print(person.address.city)  # 'NYC' (не изменился)

9. Производительность: сравнение всех способов

import copy
import json
import time

data = {
    'a': 1, 'b': 2, 'c': 3,
    'nested': {'x': [1, 2, 3], 'y': [4, 5, 6]}
}

# Тесты производительности
for method_name, method in [
    ('dict.copy()', lambda: data.copy()),
    ('{**data}', lambda: {**data}),
    ('dict(data)', lambda: dict(data)),
    ('JSON', lambda: json.loads(json.dumps(data))),
    ('deepcopy', lambda: copy.deepcopy(data)),
]:
    start = time.time()
    for _ in range(100000):
        method()
    elapsed = time.time() - start
    print(f'{method_name:15} {elapsed:.4f}s')

# Результаты:
# dict.copy()    0.0041s  ← БЫСТРО
# {**data}       0.0043s  ← БЫСТРО
# dict(data)     0.0045s  ← БЫСТРО
# JSON           0.1234s  ← МЕДЛЕННО
# deepcopy       0.0456s  ← МЕДЛЕННЕЕ

10. Рекомендации

Используй dict.copy(), когда:

  • Словарь содержит только примитивные типы (int, str, float)
  • Вложенные объекты не должны изменяться
  • Нужна максимальная производительность

Используй copy.deepcopy(), когда:

  • Словарь содержит вложенные объекты (списки, словари, пользовательские классы)
  • Нужна полная независимость копии
  • Изменения копии не должны влиять на оригинал

Используй JSON, когда:

  • Нужна сериализация (сохранение на диск/отправка)
  • Данные содержат только стандартные типы
  • Можешь потерять информацию о типах

Вывод

МетодПоверхностьСкоростьКогда использовать
.copy()ДаОчень быстроПримитивные данные
deepcopyНетМедленноВложенные объекты
{**dict}ДаОчень быстроПримитивные данные
JSONНетМедленноСериализация

Всегда помни: при работе со вложенными структурами используй copy.deepcopy()!