Комментарии (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()!