Справочник по Python · модуль 3 · списки и циклы
Глава 3.13
Двумерные списки
Двумерный список — список строк, где каждая строка тоже является списком. Такая структура представляет таблицу, игровое поле или матрицу и требует двух индексов: строки и столбца.
- Категория
- Вложенные коллекции
- Уровень
- Средний
- Связанные темы
- Списки, индексы, вложенные циклы, включения
- Зачем это знать
- Хранить и обрабатывать табличные данные
§1Список, элементы которого — списки
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
print(len(matrix))
print(len(matrix[0]))3 3
Внешний список хранит строки. Число столбцов определяется длиной конкретной строки; Python не требует, чтобы все строки имели одинаковую длину.
§2Доступ через два индекса
matrix[row][column] выполняется слева направо: сначала выбирается список-строка, затем значение внутри него.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(matrix[1]) print(matrix[1][2])
[4, 5, 6] 6
- то, что выбрал первый индекс
- то, что осталось после второго
Порядок индексов легко перепутать, и схема помогает его закрепить: первый спускается по строкам, второй идёт по столбцам внутри выбранной строки. Строка — это самостоятельный список, поэтому matrix[1] и есть полноценное значение, а не «половина обращения».
§3Перебор значений
Внешний цикл получает строки, внутренний — значения выбранной строки.
matrix = [[1, 2, 3], [4, 5, 6]] for row in matrix: for value in row: print(value, end=" ") print()
1 2 3 4 5 6
§4Перебор по индексам
Индексы нужны, если важно изменить ячейки или узнать их координаты. Длина внутреннего range() берётся у текущей строки.
matrix = [[10, 20], [30, 40]] for row_index in range(len(matrix)): for column_index in range(len(matrix[row_index])): print(row_index, column_index, matrix[row_index][column_index])
§5Создание матрицы
Цикл создаёт новый список для каждой строки. Включение записывает ту же схему компактнее.
rows = 2 columns = 3 through_loop = [] for _ in range(rows): through_loop.append([0] * columns) through_comp = [[0] * columns for _ in range(rows)] print(through_loop) print(through_comp)
[[0, 0, 0], [0, 0, 0]] [[0, 0, 0], [0, 0, 0]]
§6Почему [[0] * columns] * rows опасно
Умножение внешнего списка повторяет одну и ту же внутреннюю строку. Поэтому изменение одной ячейки проявляется во всех строках.
wrong = [[0] * 3] * 3 wrong[0][0] = 1 correct = [[0] * 3 for _ in range(3)] correct[0][0] = 1 print(wrong) print(correct)
[[1, 0, 0], [1, 0, 0], [1, 0, 0]] [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
§7Изменение структуры
matrix = [[1, 2], [3, 4]] matrix[1][1] = 40 matrix.append([5, 6]) del matrix[0] print(matrix)
[[3, 40], [5, 6]]
§8Срезы двумерного списка
Срез внешнего списка выбирает целые строки. Срез одной строки выбирает столбцы только в этой строке.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(matrix[:2]) print(matrix[1][1:])
[[1, 2, 3], [4, 5, 6]] [5, 6]
§9Поверхностная и глубокая копия
matrix.copy() создаёт новый внешний список, но внутренние строки остаются общими. Для полной независимости всех уровней применяется copy.deepcopy().
import copy matrix = [[1, 2], [3, 4]] cloned = copy.deepcopy(matrix) cloned[0][0] = 100 print(matrix) print(cloned)
[[1, 2], [3, 4]] [[100, 2], [3, 4]]
§10Три и больше измерений
Каждый дополнительный уровень списка добавляет индекс: cube[layer][row][column]. Используйте такую структуру только когда смысл каждого уровня понятен.
cube = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] print(cube[1][0][1])
6
§11Где применяются двумерные списки
- таблицы и небольшие матрицы;
- игровые поля и карты;
- пиксели небольшого изображения;
- строки табличного файла;
- расписания и сетки значений.
Для крупных числовых матриц и векторных операций обычно выбирают специализированную библиотеку вроде NumPy; обычный список остаётся удобным универсальным контейнером.
§12Частые ошибки
| Что написано | Что произойдёт | Как правильно |
|---|---|---|
matrix[column][row] | координаты поменяются местами | соблюдать выбранную схему matrix[row][column] |
[[0] * columns] * rows | все позиции внешнего списка содержат одну общую строку | [[0] * columns for _ in range(rows)] |
range(len(matrix[0])) для всех строк | на строках разной длины возможен IndexError | использовать len(matrix[row_index]) |
copy_matrix = matrix.copy() считают полной копией | изменения внутри общей строки видны в обеих матрицах | создать строки отдельно или применить copy.deepcopy() |
matrix[1, 2] | TypeError: список принимает один индекс за раз | matrix[1][2] |
§13Проверьте себя
Что сначала выбирает matrix[2][1]?
Строку с индексом 2, затем элемент этой строки с индексом 1.
Почему нельзя создавать строки выражением [[0] * 3] * 3?
Внешний список повторяет одну внутреннюю строку, поэтому её изменение видно во всех позициях.
Чем поверхностная копия отличается от глубокой?
Поверхностная копирует только внешний контейнер; глубокая рекурсивно создаёт независимые вложенные объекты.
§14Шпаргалка
matrix[row][column]for row in matrix: for value in row:[[0] * columns for _ in range(rows)]copy.deepcopy(matrix)Итог главы
Двумерный список хранит списки-строки. Доступ к ячейке требует двух последовательных индексов, а полный перебор — двух циклов. Каждую строку при создании нужно формировать отдельно; для полной копии вложенной структуры используется глубокое копирование.