Python
Двумерный массив в Python
Когда данные идут строками и столбцами, в Python удобно хранить их как список списков: одна строка внешнего списка содержит отдельный внутренний список
Разберем двумерный список как таблицу: как обратиться к строке и ячейке, как пройти всю матрицу циклом, как создать поле нужного размера и как не сломаться на неровных строках
Базовый пример
Пример матрицы 3 на 3:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
print(matrix[0][0])
print(matrix[1][2])
Первый индекс выбирает строку, второй — элемент внутри строки
Разбор по шагам
Двумерный список читается как таблица. Внешний индекс выбирает строку, внутренний индекс выбирает элемент внутри этой строки. Запись matrix[1][2] означает: возьми вторую строку и третий элемент в ней
Перед обработкой таблицы проверьте два момента: есть ли строки вообще и одинаковая ли у них длина. В учебных примерах матрица обычно ровная, а в реальных данных одна строка может оказаться короче другой. Поэтому обход через for row in matrix часто надежнее, чем обращение по фиксированным индексам
По этой теме полезно отдельно посмотреть Составьте еженедельное расписание занятий, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Аннотации типов для декораторов в Python, чтобы расширить контекст и сравнить подходы
Практические варианты
Перебор всех элементов
matrix = [[1, 2], [3, 4]]
for row in matrix:
for value in row:
print(value)
Сумма по строкам
matrix = [[1, 2], [3, 4]]
for row in matrix:
print(sum(row))
Частые ошибки
Главная ошибка — создать строки через [[0] 3] 3. В таком варианте внутренние списки связаны между собой. Безопаснее писать [[0 for _ in range(3)] for _ in range(3)]
Как проверить результат
- запустить код на обычном примере
- проверить пустой список
- проверить список из одного элемента
- проверить повторы
- объяснить, меняется исходный список или создается новый
- вынести действие в функцию, если оно повторяется
Создание поля заданного размера
Для игрового поля или таблицы часто нужно создать сетку из нулей
rows = 3
cols = 3
field = [[0 for _ in range(cols)] for _ in range(rows)]
field[1][1] = 5
print(field)
Каждая строка здесь отдельный список, поэтому изменение одной ячейки не меняет весь столбец
Поиск значения в двумерном списке
matrix = [
[1, 2, 3],
[4, 5, 6],
]
target = 5
for row_index, row in enumerate(matrix):
for col_index, value in enumerate(row):
if value == target:
print(row_index, col_index)
Проверьте значение, которого нет. В реальной задаче для такого случая лучше заранее подготовить сообщение или вернуть None
Где это пригодится
Двумерный список удобен для шахматной доски, крестиков-ноликов, таблицы оценок, расписания по дням и часам, матрицы чисел. Если строки имеют разную длину, обязательно проверяйте длину каждой строки перед обращением по индексу
Практика на закрепление
Сделайте маленькую игру в крестики-нолики на уровне данных. Пока не нужен интерфейс: достаточно хранить поле и уметь поставить символ в строку и столбец
field = [["." for _ in range(3)] for _ in range(3)]
row = 1
col = 1
field[row][col] = "X"
for line in field:
print(" ".join(line))
Проверьте координаты 0, 0, 2, 2 и выход за границы. Для выхода за границы добавьте условие:
if 0 <= row < len(field) and 0 <= col < len(field[row]):
field[row][col] = "X"
Так двумерный массив становится не «таблицей в теории», а структурой для конкретного сценария: есть строки, столбцы, координаты и проверка допустимого хода
Практика: поле бронирования мест
Сделайте маленькую таблицу мест в зале. Ноль означает свободное место, единица — занятое. Функция бронирования должна проверить границы таблицы и не падать, если пользователь передал неверную строку или колонку
def create_hall(rows, cols):
return [[0 for _ in range(cols)] for _ in range(rows)]
def book_seat(hall, row, col):
if row < 0 or row >= len(hall):
return False
if col < 0 or col >= len(hall[row]):
return False
if hall[row][col] == 1:
return False
hall[row][col] = 1
return True
hall = create_hall(3, 4)
print(book_seat(hall, 1, 2))
print(book_seat(hall, 5, 1))
print(hall)
Такой пример хорошо показывает смысл двумерного списка: есть строки, столбцы, проверка границ и изменение конкретной ячейки
Где двумерный список удобнее обычного
Двумерный список нужен, когда данные естественно читаются как строки и столбцы. Это поле игры, расписание, таблица оценок, места в зале, шахматная доска, сетка пикселей или отчет по дням недели. Если каждую строку можно обработать одинаково, структура list внутри list подходит хорошо
Перед написанием кода полезно нарисовать маленькую таблицу на бумаге и подписать индексы. Так быстрее становится понятно, почему matrix[0][2] — это первая строка и третий элемент, а не наоборот. После этого проверьте обход всей таблицы, изменение одной ячейки и случай пустой таблицы. Эти три проверки закрывают большинство ошибок новичка
Проверка границ таблицы
Перед обращением к ячейке полезно проверять не только номер строки, но и номер столбца внутри выбранной строки. Это особенно важно, если строки могут быть разной длины
def get_cell(matrix, row, col):
if row < 0 or row >= len(matrix):
return None
if col < 0 or col >= len(matrix[row]):
return None
return matrix[row][col]
print(get_cell([[1, 2], [3]], 1, 0))
print(get_cell([[1, 2], [3]], 1, 1))
Такая проверка делает работу с таблицей спокойнее: программа не падает, а возвращает понятный результат для отсутствующей ячейки
Итоговая практика: сумма по строкам таблицы
Сделайте функцию, которая получает таблицу оценок и возвращает сумму по каждой строке. Так вы закрепите вложенный цикл и научитесь получать новый список результатов из двумерного списка
def row_totals(matrix):
totals = []
for row in matrix:
totals.append(sum(row))
return totals
grades = [
[5, 4, 5],
[3, 4, 4],
[5, 5, 5],
]
print(row_totals(grades))
print(row_totals([]))
Затем проверьте строку с пустым списком: row_totals([[5, 4], []]). Результат покажет, что функция спокойно обрабатывает неидеальные данные