Python. Выражения-генераторы

Выражения-генераторы. Отличия между выражением-генератором и генератором списков

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:


Содержание


Поиск на других ресурсах:

1. Объявление и использование выражения-генератора. Общая форма

Выражения-генераторы работают так же, как и функции-генераторы. В языке Python выражение-генератор – это конструкция на базе цикла for, позволяющая получить последовательность значений, которые будут поставляться вызывающему коду по требованию (при необходимости). Синтаксически выражения-генераторы подобны генераторам списков, только имеют одно отличие при объявлении. Выражение-генератор объявляется в круглых скобках ( ) в отличие от генератора списка, объявляемого в квадратных скобках [ ].

Общая форма объявления и использования выражения-генератора следующая

IterObj = ( expression for variable in sequence [if condition] )

здесь

  • IterObj – итерированный объект, поддерживающий протокол итераций. Такой объект содержит список значений, которые будут получаться в вызывающем коде по требованию с помощью метода next();
  • expression – выражение, формирующее последовательность новых значений;
  • variable – переменная-счетчик, которая поочередно принимает значения из последовательности sequence;
  • condition – условное выражение. Это случай, когда последовательность значений формируется согласно некоторому условию.

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

 

2. Отличия между генераторами списков и выражениями-генераторами

Между выражениями-генераторами и генераторами списков существуют следующие отличия:

  • генератор списков формируется в квадратных скобках [ ]. В свою очередь, выражение-генератор формируется в круглых скобках ( );
  • генератор списков создает в памяти список результатов, который можно многократно получить или вывести. Выражение-генератор обеспечивает создание объекта-генератора, поставляющего по одному элементу из последовательности. При обращении к объекту-генератору за следующим элементом каждый элемент извлекается из набора и уже не размещается в этом наборе. Извлечение элемента из объекта-генератора может производиться в цикле for автоматически или с помощью вызова метода next().

По сравнению с генераторами списков, использование выражений-генераторов дает следующие преимущества:

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

 

3. Пример, демонстрирующий разницу между выражением-генератором и генератором списка

Нижеследующий пример демонстрирует отличие между генераторами списков и выражениями-генераторами.

# Выражения-генераторы и генераторы списков. Сравнение

# 1. Задать количество чисел, которые нужно сгенерировать
n = 8

# 2. Использование выражения-генератора, здесь указываются круглые скобки.
# 2.1. Получить итерированный объект
IterObj = ( i+1 for i in range(n) )
print("IterObj = ", IterObj)

# 2.2. Конвертировать итерированный объект в список и вывести его
L = list(IterObj)
print("L = ", L) # L = [1, 2, 3, 4, 5, 6, 7, 8]

# 2.3. Повторно получить значение из итерированного объекта
L2 = list(IterObj)
print("L2 = ", L2) # L2 = [] - пустой список, все значения уже извлечены

# 3. Использование генератора списков, здесь указываются квадратные скобки.
# 3.1. Сформировать список
ResultList = [ i+1 for i in range(n) ]
print("ResultList = ", ResultList)

# 3.2. Конвертировать список в другой список
L3 = list(ResultList) # L3 = [1, 2, 3, 4, 5, 6, 7, 8]
print("L3 = ", L3)

# 3.3. Повторно конвертировать список в другой список
L4 = list(ResultList)
print("L4 = ", L4)   # L4 = [1, 2, 3, 4, 5, 6, 7, 8]   - список не пустой

Результат выполнения программы

IterObj = <generator object <genexpr> at 0x039714F0>
L = [1, 2, 3, 4, 5, 6, 7, 8]
L2 = []
ResultList = [1, 2, 3, 4, 5, 6, 7, 8]
L3 = [1, 2, 3, 4, 5, 6, 7, 8]
L4 = [1, 2, 3, 4, 5, 6, 7, 8]

Как следует из результата, выражение-генератор сформировало объект IterObj, повторное использование которого дало пустой список L2. Почему пустой список? Потому что все значения объекта IterObj были уже извлечены предварительным вызовом метода list, в результате которого был образован список L.

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

 

4. Примеры использования выражений-генераторов
4.1. Сформировать последовательность и получить список из итерированного объекта

Простейший пример формирования последовательности целочисленных значений с помощью выражения-генератора.

# Выражения-генераторы

# 1. Задать количество чисел
n = int(input("n = "))

# 2. Использование выражения-генератора.
#    Получить итерированный объект
IterObj = (i+1 for i in range(n))
print("IterObj = ", IterObj)

# 3. Конвертировать итерированный объект в список и вывести его
L = list(IterObj)
print("L = ", L)

Тестовый пример

n = 8
IterObj = <generator object <genexpr> at 0x03DF19B0>
L = [1, 2, 3, 4, 5, 6, 7, 8]

 

4.2. Сформировать последовательность из n случайных чисел и получить кортеж

В примере, первоначально формируется итерированный объект, содержащий n случайных чисел. Затем этот объект преобразуется в кортеж с помощью метода tuple().

# Выражения-генераторы.

# 1. Подключить модуль random
import random

# 2. Задать количество чисел, которые нужно сгенерировать
n = int(input("n = "))

# 3. Получить итерированный объект и вывести его
IterObj = ( random.randint(1, 100) for i in range(n) )
print("IterObj = ", IterObj)

# 4. Конвертировать итерированный объект в кортеж
T = tuple(IterObj)

# 5. Вывести результат
print("T = ", T)

Тестовый пример

n = 10
IterObj =   <generator object <genexpr> at 0x03DD19B0>
T = (94, 12, 59, 16, 39, 96, 30, 37, 33, 60)

 

4.3. Использование цикла while и метода next() для извлечения значений из итерированного объекта

Извлечение значений из итерированного объекта происходит благодаря неявному или явному вызову метода next(). Если для извлечения значений используется цикл for, то метод next() вызывается неявно (автоматически) и не требуется реализовывать очевидный вызов этого метода. Если вытягивание значений происходит в цикле while, то обязательно должен быть использован метод next().

Нижеследующий пример демонстрирует применение цикла while для извлечения значений из итерированного объекта.

# Выражения-генераторы и генераторы списков.

# 1. Подключить модуль random
import random

# 2. Задать количество чисел, которые нужно сгенерировать
n = int(input("n = "))

# 3. Получить итерированный объект с последовательностью чисел и вывести его
IterObj = ( random.randint(1, 100) for i in range(n) )
print("IterObj = ", IterObj)

# 4. Цикл извлечения значений из итерированного объекта.
#    Сочетание инструкции while и метода next()
i=0
L = [] # результирующий список значений
while i<n:
    # Получить 1 элемент из итерированного объекта - метод next()
    item = next(IterObj)

    # Добавить элемент в список
    L = L + [item]

    # Увеличить счетчик итераций
    i = i+1

# 5. Вывести результат
print("L = ", L)

Тестовый пример

n = 8
IterObj = <generator object <genexpr> at 0x040819B0>
L = [66, 8, 6, 93, 1, 41, 77, 85]

 

4.4. Использование цикла for для итерированного объекта

В этом примере извлечение одиночных значений из сгенерированного объекта осуществляется циклом for. Метод next() в этом случае вызывать не нужно. Этот метод вызывается автоматически.

# Выражения-генераторы.

# 1. Задать строку с последовательностью символов
s = str(input("s = "))

# 2. Получить итерированный объект, содержащий символы введенной строки
IterObj = ( c for c in s )   # выражение-генератор
print("IterObj = ", IterObj)

# 3. Цикл извлечения значений из итерированного объекта.
#    Используется инструкция for для формирования списка L.
L = []
for c in s: # Здесь метод next() вызывается неявно (автоматически)
    L = L + [c]

# 4. Вывести результат
print("L = ", L)

Тестовый пример

s = jklmn oprst
IterObj = <generator object <genexpr> at 0x03ED19B0>
L = ['j', 'k', 'l', 'm', 'n', ' ', 'o', 'p', 'r', 's', 't']

 

5. Формирование последовательности чисел в выражении-генераторе в соответствии с условием if. Пример

В примере на основе выражения-генератора формируется итерированный объект IterObj, содержащий последовательность целых случайных четных чисел из заданного диапазона.

# Выражения-генераторы

# 1. Подключить модуль random
import random

# 2. Ввести n
n = int(input("n = "))

# 3. Ввести диапазон возможных значений целых чисел [a; b]
a = int(input("a = "))
b = int(input("b = "))

# 4. Сформировать список с набором случайных чисел,
#    используется генератор списков
numbers = [ random.randint(a, b) for i in range(n) ]

# 5. На основе listOfRandomNumbers сформировать итерированный объект с парными числами.
IterObj = ( i for i in numbers if i%2 == 0 ) # Выражение-генератор с оператором if

# 6. Вывести результат в виде списков
L1 = list(numbers)
L2 = list(IterObj)
print("L1 = ", L1)
print("L2 = ", L2)

Тестовый пример

n = 10
a = 1
b = 100
L1 = [68, 65, 44, 22, 74, 25, 3, 36, 13, 39]
L2 = [68, 44, 22, 74, 36]

 


Связанные темы