Добрый, добрый Python
December 25, 2023

Stepik: Вложенные циклы и вложенные генераторы списков

a = [(i, j) for i in range(3) for j in range(4)]
print(a)
# Вывод: [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
a = [(i, j) for i in range(3) if i % 3 == 0 for j in range(4) if j % 2 == 0]
print(a)
# Вывод: [(0, 0), (0, 2)]

ЭТО ТОЖЕ САМОЕ, ЧТО:

a = [(i, j)
     for i in range(3) if i % 3 == 0
     for j in range(4) if j % 2 == 0
     ]
print(a)
# Вывод: [(0, 0), (0, 2)]

Используя такой подход легко сформировать таблицу умножения

a = [f'{i} * {j} = {i * j}|'
     for i in range(2, 11)
     for j in range(1, 11)
     ]
print(*a)
# Вывод: 2 * 1 = 2| 2 * 2 = 4| 2 * 3 = 6| 2 * 4 = 8| и тд таблица умножения

Можно двумерный список превратить в одномерный:

matrix = [[0, 1, 2, 3],
          [10, 11, 12, 13],
          [20, 21, 22, 23]
          ]
a = [x
     for row in matrix
     for x in row
     ]
print(a)
# Вывод: [0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23]
Во вложенных циклах можно использовать переменные, объявленные в этих циклах ранее

Если принимать то, что синтаксис вложенного генератора списков такой: [<оператор> for <счетчик> in <итерируемый объект>] тогда что мешает использовать вместо оператора другой генератор списков [[генератор списка] for <переменная> in <итерируемый объект>]

M, N = 3, 4
matrix = [[a for a in range(M)] for b in range(N)]
print(matrix)
# Вывод: [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]

Результат вполне очевиден. Сначала отрабатывает первый (внешний) генератор списка и переменная b = 0. Затем, выполнение переходит к вложенному генератору, который выдает список [0, 1, 2]. Этот список помещается как первый элемент основного списка. Далее, снова отрабатывает первый генератор и b принимает значение 1. После этого переходим к вложенному генератору, который возвращает такой же список [0, 1, 2]. И так пока не закончится работа первого генератора. В итоге, видим список, в который вложены четыре других списка.

Этот подход может пригодится для изменения значений двумерного списка.

Давайте предположим, что у нас есть вот такой список и мы хотим возвести все его значения в квадрат.

A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
A = [[x ** 2 for x in row] for row in A]
print(A)
# row - переменная для вложенных списков, а х - для каждого значения в списке
# Вывод: [[1, 4, 9], [16, 25, 36], [49, 64, 81]]

Другой пример – это транспонирование матрицы A (то есть, замена строк на столбцы) с использованием вложенных генераторов. Сделать это можно следующим образом:

A = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
A = [[row[i] for row in A] for i in range(len(A[0]))]
print(A)
# Вывод: [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Сначала значение i = 0, а переменная row[i] пробегает первые значения строк матрицы A. В результате формируется первая строка транспонированной матрицы. Далее, i увеличивается на 1 и row[i] пробегает уже вторые элементы строк матрицы A. Получаем вторую строку транспонированной матрицы. И так делаем для всех столбцов. На выходе формируется транспонированная матрица.

Так же можно поместить генератор списка в качестве итерируемого объекта [<оператор> for <счетчик> in [генератор списка]]

g = [u ** 2 for u in [x + 1 for x in range(5)]]

Данная конструкция создает список от 1 до 5 ([x + 1 for x in range(5)]), а затем возводит каждое значение в квадрат

g = [u ** 2 for u in [x + 1 for x in range(5)]]
print(g)
# Вывод: [1, 4, 9, 16, 25]

ТЕКСТ ЛЕКЦИИ