Генератор в Python — це функція, що повертає ітератор, який під час ітерації генерує послідовність значень. Генератори корисні, коли нам потрібно отримати велику послідовність значень, але ми не хочемо зберігати їх всі в пам’яті відразу.
Створення генератора в Python
Як і зі звичайними функціями, функцію-генератор в Python можна визначити за допомогою ключового слова def, але замість оператора return використовується оператор yield.
|
1 2 3 |
def generator_name(arg): # Тіло генератора yield something |
Тут ключове слово yield використовується для повернення значення з генератора. Коли викликається генератор, його тіло (код) не виконується відразу. Натомість повертається об’єкт генератора, який можна ітерувати для отримання значень.
Ось приклад функції-генератора, що генерує послідовність чисел:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def my_generator(n): # Ініціалізуємо лічильник value = 0 # Цикл виконується доти, доки лічильник не стане менше n while value < n: # Повертаємо поточне значення лічильника yield value # Збільшуємо лічильник value += 1 # Виконуємо ітерацію генератора for value in my_generator(3): # Виводимо кожне значення, отримане від генератора print(value) |
Результат:
0
1
2
Тут функція-генератор my_generator() приймає як аргумент ціле число n і видає послідовність чисел від 0 до n-1. Ключове слово yield використовується для отримання значення з генератора та зупинки виконання функції-генератора до запиту наступного значення.
Цикл for ітерує об’єкт генератора, створений функцією my_generator(), а оператор print виводить на екран кожне значення, видане генератором. Ми також можемо створити об’єкт генератора з функції-генератора, викликавши її так само, як будь-яку іншу функцію:
|
1 2 3 4 |
generator = my_range(3) print(next(generator)) # 0 print(next(generator)) # 1 print(next(generator)) # 2 |
Генераторні вирази в Python
В Python генераторний вираз — це лаконічний спосіб створення об’єкта генератора. Він схожий на абстракцію списків, але замість створення списку він створює об’єкт-генератор, який можна ітерувати для отримання значень.
Генераторний вираз в Python має наступний синтаксис:
|
1 |
(вираз for елемент in ітератор) |
Тут вираз — це значення, яке буде повернено для кожного елемент в ітератор.
Генераторний вираз створює об’єкт генератора, який при ітерації видає значення виразу для кожного елемента в ітераторі по одному за один раз.
Розглянемо приклад генераторного виразу в Python:
|
1 2 3 4 5 6 |
# Створюємо об'єкт генератора squares_generator = (i * i for i in range(5)) # Ітерація по генератору та вивід значень на екран for i in squares_generator: print(i) |
Результат:
0
1
4
9
16
Тут ми створили об’єкт генератора, який при ітерації підноситиме до квадрата кожне число з послідовності від 0 до 4. Потім ми використали цикл for для виконання ітерацій над генератором і виводу значень на екран.
Користь від генераторів в Python
Є декілька причин, чому генератори є корисною конструкцією в Python.
1. Простота реалізації.
Генератори створювати простіше, ніж ітератори.
Ось приклад програми піднесення значень до квадрата на кожній ітерації, використовуючи клас ітератора:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n > self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result |
А тепер те саме завдання, але з використанням генератора:
|
1 2 3 4 5 |
def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1 |
Реалізація за допомогою генератора вийшла лаконічнішою та “чистішою”.
2. Ефективність використання пам’яті.
Звичайна функція, що повертає послідовність, створює всю послідовність у пам’яті, перш ніж повернути результат. Це проблема, коли кількість елементів у послідовності величезна.
Використання генераторів у подібних випадках не вимагає використання великої кількості пам’яті та є кращим варіантом, оскільки віддає лише один елемент за раз.
3. Представлення нескінченного потоку даних.
Генератори є хорошим засобом для представлення нескінченного потоку даних. Нескінченні потоки неможливо зберігати в пам’яті, а оскільки генератори віддають лише один елемент за раз, вони можуть представляти нескінченний потік даних.
Наступна функція-генератор може генерувати всі парні числа (принаймні теоретично):
|
1 2 3 4 5 |
def all_even(): n = 0 while True: yield n n += 2 |
4. Обробка кількох операцій.
Декілька генераторів можна використовувати для обробки відразу кількох операцій. Найкраще це проілюструвати на прикладі.
Припустимо, у нас є генератор, який генерує послідовність чисел Фібоначчі. Та є ще один генератор для піднесення чисел до квадрата.
Якщо ми хочемо дізнатися суму квадратів чисел Фібоначчі, ми можемо зробити це, поєднавши разом вихідні дані генераторів:
|
1 2 3 4 5 6 7 8 9 10 11 |
def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10)))) |
Результат:
4895

(18 оцінок, середня: 4,94 з 5)