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]

 


Споріднені теми