Отладка коллекций: 14 типичных ошибок
IndexError, KeyError, путаница append/extend, aliasing, поверхностная копия и другие ловушки — в одном месте, с примерами и разбором.
14 типичных ошибок при работе со списками, кортежами, множествами и словарями — каждая с примером и объяснением. Первые две разберём отдельно с диаграммой, остальные — компактно.
IndexError — визуально
items = ['A', 'B', 'C']
print(items[3])
# IndexError: list index out of range
KeyError — визуально
student = {'name': 'Anna'}
print(student['email'])
# KeyError: 'email'
Ещё 12 типичных ошибок
items = ['A', 'B', 'C']
print(items[3])
Допустимые индексы — от 0 до len(items)-1 = 2. Индекс 3 не существует.
student = {'name': 'Anna'}
print(student['email'])
Такого ключа в словаре нет. Безопаснее: student.get('email').
fruits = ['яблоко', 'банан']
fruits.remove('вишня')
'вишня' нет в списке — remove() ищет по значению и не находит его. Проверьте через in перед вызовом.
bad = {[1, 2]: 'значение'}
Список нельзя использовать как ключ словаря (или элемент множества) — он изменяем, а значит не хешируем (§11.16).
a = [1, 2]
a.append([3, 4])
print(a) # [1, 2, [3, 4]], а не [1, 2, 3, 4]
append() всегда добавляет один объект. Чтобы добавить элементы ИЗ списка — нужен extend().
numbers = [3, 1, 2]
numbers = numbers.sort()
print(numbers) # None
sort() мутирует список на месте и возвращает None. Присваивание результата обратно стирает список.
a = [1, 2, 3]
b = a
b.append(4)
print(a) # тоже [1, 2, 3, 4]!
b = a не копирует список — оба имени указывают на один и тот же объект (§11.9). Нужно b = a.copy().
original = [[1, 2]]
copy_ = original.copy()
copy_[0][0] = 99
print(original) # [[99, 2]] — тоже изменился!
.copy() копирует только внешний список — вложенные списки остаются общими (§11.10). Нужен copy.deepcopy().
grid = [[0] * 3] * 3
grid[0][0] = 1
print(grid) # изменились ВСЕ строки
*3 копирует ссылку на один и тот же внутренний список трижды. Используйте [[0]*3 for _ in range(3)].
items = [1, 2, 3, 4]
for x in items:
if x % 2 == 0:
items.remove(x)
print(items) # не то, что ожидали
Удаление элемента сдвигает индексы прямо во время перебора — часть значений пропускается. Стройте новый список или перебирайте копию.
empty = {}
print(type(empty)) # <class 'dict'>, а не set!
Пустой словарь и пустое множество выглядят по-разному только в непустом виде. Пустое множество — всегда set().
point = (42)
print(type(point)) # <class 'int'>
Кортеж создаёт запятая, а не скобки. Нужно (42,).
student = {'name': 'Anna'}
print('Anna' in student) # False!
'Anna' — значение, а не ключ. Для проверки значений: 'Anna' in student.values().
matrix = [[1, 2, 3], [4, 5, 6]]
print(matrix[2][0]) # IndexError
У matrix всего 2 строки (индексы 0 и 1) по 3 столбца — сначала строка, потом столбец, не наоборот.
Метод отладки: трасса по шагам
- Перед запуском предскажите, что будет в переменной — потом сравните с реальным выводом.
- Для ошибок с индексами/ключами явно выпишите допустимый диапазон или список ключей.
- Если результат «странный, но без ошибки» — подозревайте aliasing или поверхностную копию.
- print() после каждого шага — самый надёжный способ найти момент, где ожидание разошлось с реальностью.