Модуль 3 · Списки и циклы

Справочник по Python · модуль 3 · списки и циклы

Глава 3.13

3.13

Двумерные списки

Двумерный список — список строк, где каждая строка тоже является списком. Такая структура представляет таблицу, игровое поле или матрицу и требует двух индексов: строки и столбца.

Категория
Вложенные коллекции
Уровень
Средний
Связанные темы
Списки, индексы, вложенные циклы, включения
Зачем это знать
Хранить и обрабатывать табличные данные

§1Список, элементы которого — списки

main.pyтаблица 3 × 3
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
print(len(matrix))
print(len(matrix[0]))
Выводтри строки и три столбца
3
3

Внешний список хранит строки. Число столбцов определяется длиной конкретной строки; Python не требует, чтобы все строки имели одинаковую длину.

§2Доступ через два индекса

matrix[row][column] выполняется слева направо: сначала выбирается список-строка, затем значение внутри него.

main.pyстрока 1, столбец 2
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[1])
print(matrix[1][2])
Выводсначала строка, затем число
[4, 5, 6]
6
Схемасначала строка, потом столбец
столбец [0] [1] [2] строка [0] 1 2 3 строка [1] 4 5 6 строка [2] 7 8 9 matrix[1] весь список-строка matrix[1][2]
  • то, что выбрал первый индекс
  • то, что осталось после второго

Порядок индексов легко перепутать, и схема помогает его закрепить: первый спускается по строкам, второй идёт по столбцам внутри выбранной строки. Строка — это самостоятельный список, поэтому matrix[1] и есть полноценное значение, а не «половина обращения».

§3Перебор значений

Внешний цикл получает строки, внутренний — значения выбранной строки.

main.pyтабличный вывод
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() берётся у текущей строки.

main.pyкоординаты каждой ячейки
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Создание матрицы

Цикл создаёт новый список для каждой строки. Включение записывает ту же схему компактнее.

main.pyдва независимых варианта
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)
Выводдве матрицы 2 × 3
[[0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 0]]

§6Почему [[0] * columns] * rows опасно

Умножение внешнего списка повторяет одну и ту же внутреннюю строку. Поэтому изменение одной ячейки проявляется во всех строках.

main.pyобщая строка и независимые строки
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Изменение структуры

main.pyячейка, добавление и удаление строки
matrix = [[1, 2], [3, 4]]
matrix[1][1] = 40
matrix.append([5, 6])
del matrix[0]
print(matrix)
Выводдве оставшиеся строки
[[3, 40], [5, 6]]

§8Срезы двумерного списка

Срез внешнего списка выбирает целые строки. Срез одной строки выбирает столбцы только в этой строке.

main.pyстроки и часть строки
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().

main.pyполностью независимая матрица
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]. Используйте такую структуру только когда смысл каждого уровня понятен.

main.pyслой 1, строка 0, столбец 1
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)

Итог главы

Двумерный список хранит списки-строки. Доступ к ячейке требует двух последовательных индексов, а полный перебор — двух циклов. Каждую строку при создании нужно формировать отдельно; для полной копии вложенной структуры используется глубокое копирование.

Глава 3.13 · Двумерные списки · Справочник по PythonАвтор: Макаров Максим Николаевич