Вложенные списки. Часть 2

  1. Создание вложенных списков
  2. Считывание вложенных списков
  3. Перебор и вывод элементов вложенного списка
  4. Обработка вложенных списков
  5. Задачи

Аннотация. Урок посвящен работе с вложенными списками.

Создание вложенных списков

Для создания вложенного списка можно использовать литеральную форму записи – перечисление элементов через запятую в квадратных скобках:

my_list = [[0], [1, 2], [3, 4, 5]]

Иногда нужно создать вложенный список, заполненный по определенному правилу – шаблону. Например, список длиной n, содержащий списки длиной m, каждый из которых заполнен нулями.

Рассмотрим несколько способов решения задачи.

Способ 1. Создадим пустой список, потом n раз добавим в него новый элемент – список длины m, составленный из нулей:

n, m = int(input()), int(input())    # считываем значения n и m
my_list = []

for _ in range(n):
    my_list.append([0] * m)

print(my_list)

Если ввести значения n = 3, m = 5, то результатом работы такого кода будет:

[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Если передать значения n = 5, m = 3, то результатом работы такого кода будет:

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Способ 2. Сначала создадим список из n элементов (для начала просто из n нулей). Затем сделаем каждый элемент списка ссылкой на другой список из m элементов, заполненный нулями:

n, m = int(input()), int(input())  # считываем значения n и m
my_list = [0] * n

for i in range(n):
    my_list[i] = [0] * m

print(my_list)

Способ 3. Можно использовать генератор списка: создадим список из n элементов, каждый из которых будет списком, состоящих из m нулей:

n, m = int(input()), int(input())  # считываем значения n и m

my_list = [[0] * m for _ in range(n)]

print(my_list)

В этом случае каждый элемент создается независимо от остальных (заново конструируется вложенный список [0] * m для заполнения очередного элемента списка).

Обратите внимание, что очевидное решение, использующее операцию умножения списка на число (операция повторения), оказывается неверным:

n, m = int(input()), int(input())  # считываем значения n и m

my_list = [[0] * m ] * n

print(my_list)

В этом легко убедиться, если присвоить элементу my_list[0][0] любое значение, например, 17, а затем вывести список на печать:

n, m = int(input()), int(input())

my_list = [[0] * m ] * n
my_list[0][0] = 17

print(my_list)

Если ввести значения n = 5, m = 3, то результатом работы такого кода будет:

[[17, 0, 0], [17, 0, 0], [17, 0, 0], [17, 0, 0], [17, 0, 0]]

То есть, изменив значение элемента списка my_list[0][0], мы также изменили значения элементов my_list[1][0]my_list[2][0]my_list[3][0]my_list[4][0].

Причина такого поведения кроется в самой природе списков (тип  list). В Python списки – ссылочный тип данных. Конструкция [0] * m возвращает ccылку на список из m нулей. Повторение этого элемента создает список из n ссылок на один и тот же список.

Вложенный список нельзя создать при помощи операции повторения (умножения списка на число). Для корректного создания вложенного списка мы используем способы 1−31-3, отдавая предпочтение способу 33.

Считывание вложенных списков


Если элементы списка вводятся через клавиатуру (каждая строка на отдельной строке, всего n строк, числа в строке разделяются пробелами), для ввода списка можно использовать следующий код:

n = 4                                         # количество строк (элементов)
my_list = []

for _ in range(n):
    elem = [int(i) for i in input().split()]  # создаем список из элементов строки
    my_list.append(elem)

В этом примере мы используем списочный метод append(), передавая ему в качестве аргумента другой список. Так у нас получается список списков.

В результате, если на вход программе подаются строки:

2 4
6 7 8 9
1 3
5 6 5 4 3 1

то в переменной my_list будет храниться список:

[[2, 4], [6, 7, 8, 9], [1, 3], [5, 6, 5, 4, 3, 1]]

Не забывайте, что метод split() возвращает список строк, а не чисел. Поэтому мы предварительно сконвертировали строку в число, с помощью вызова функции int().

Также следует помнить отличие работы списочных методов append() и extend().

Следующий код:

n = 4
my_list = []

for _ in range(n):
    elem = [int(i) for i in input().split()]
    my_list.extend(elem)

создает одномерный (!) список, а не вложенный. В переменной my_list будет храниться список:

[2, 4, 6, 7, 8, 9, 1, 3, 5, 6, 5, 4, 3, 1]

Перебор и вывод элементов вложенного списка


Как мы уже знаем, для доступа к элементу списка указывают индекс этого элемента в квадратных скобках. В случае двумерных вложенных списков надо указать два индекса (каждый в отдельных квадратных скобках), в случае трехмерного списка — три индекса и т. д.

Рассмотрим программный код:

my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

print(my_list[0][0])
print(my_list[1][2])
print(my_list[2][1])

Результатом работы такого кода будет:

1
6
8

Когда нужно перебрать все элементы вложенного списка (например, чтобы вывести их на экран), обычно используются вложенные циклы.

Рассмотрим программный код:

my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for i in range(len(my_list)):
    for j in range(len(my_list[i])):
        print(my_list[i][j], end=' ')  # используем необязательный параметр end
    print()                            # перенос на новую строку

Результатом работы такого кода будет:

1 2 3 
4 5 6 
7 8 9 

Вызов функции print() с пустыми параметрами нужен для того, чтобы переносить вывод на новую строку, после того как будет распечатан очередной элемент (список) вложенного списка.

В предыдущем примере мы перебирали индексы элементов, а можно сразу перебирать сами элементы вложенного списка:

my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for row in my_list:
    for elem in row:
        print(elem, end=' ')
    print()

Результатом работы такого кода будет:

1 2 3 
4 5 6 
7 8 9 

Перебор элементов вложенного списка по индексам дает нам больше гибкости для вывода данных. Например, поменяв порядок переменных i и j, мы получаем иной тип вывода:

my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for i in range(len(my_list)):
    for j in range(len(my_list[i])):
        print(my_list[j][i], end=' ')  # выводим my_list[j][i] вместо my_list[i][j]
    print()

Результатом работы такого кода будет:

1 4 7 
2 5 8 
3 6 9 

Обработка вложенных списков


Для обработки элементов вложенного списка, так же как и для вывода его элементов на экран, как правило, используются вложенные циклы.

Используем вложенный цикл для подсчета суммы всех чисел в списке:

my_list = [[1, 9, 8, 7, 4], [7, 3, 4], [2, 1]]

total = 0
for i in range(len(my_list)):
    for j in range(len(my_list[i])):
        total += my_list[i][j]

print(total)

Или то же самое с циклом не по индексу, а по значениям:

my_list = [[1, 9, 8, 7, 4], [7, 3, 4], [2, 1]]

total = 0
for row in my_list:
    for elem in row:
        total += elem

print(total)

Таким образом, можно обработать элементы вложенного списка практически в любом языке программирования. В Python, однако, можно упростить код, если использовать встроенную функцию sum(), которая принимает список чисел и возвращает его сумму. Подсчет суммы с помощью функции sum() выглядит так:

my_list = [[1, 9, 8, 7, 4], [7, 3, 4], [2, 1]]

total = 0
for row in my_list:  # в один цикл
    total += sum(row)
print(total)

Названия переменных row (строка) и elem (элемент) удобно использовать при переборе вложенного списка по значениям. Названия переменных i и j используются при переборе вложенного списка по индексам.