Як - і чому - вам слід використовувати генератори Python

Генератори були важливою частиною Python з тих пір, як вони були представлені з PEP 255.

Функції генератора дозволяють оголосити функцію, яка поводиться як ітератор.

Вони дозволяють програмістам швидко, легко і чисто зробити ітератор.

Ви можете запитати, що таке ітератор?

Ітератора є об'єктом , який може повторюватися (петельний) на. Він використовується для абстрагування контейнера даних, щоб він поводився як ітерабельний об’єкт. Напевно, ви вже використовуєте кілька ітеративних об'єктів щодня: рядки, списки та словники, щоб назвати декілька.

Ітератор визначається класом, який реалізує протокол Iterator . Цей протокол шукає два методи в класі: __iter__і __next__.

Вау, відступи назад. Чому ви взагалі хочете зробити ітератори?

Економія місця в пам'яті

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

Ледача оцінка корисна, коли у вас є дуже великий набір даних для обчислення. Це дозволяє негайно розпочати використання даних, поки обчислюється весь набір даних.

Скажімо, ми хочемо отримати всі прості числа, менші за максимальне число.

Спочатку визначаємо функцію, яка перевіряє, чи є число простим:

def check_prime(number): for divisor in range(2, int(number ** 0.5) + 1): if number % divisor == 0: return False return True

Потім ми визначаємо клас ітератора, який включатиме методи __iter__and __next__:

class Primes: def __init__(self, max): self.max = max self.number = 1
 def __iter__(self): return self
 def __next__(self): self.number += 1 if self.number >= self.max: raise StopIteration elif check_prime(self.number): return self.number else: return self.__next__()

Primesінстанціюється з максимальним значенням. Якщо наступна проста кількість більша або дорівнює max, ітератор викликає StopIterationвиняток, який закінчує ітератор.

Коли ми запитуємо наступний елемент в ітераторі, він збільшиться numberна 1 і перевірить, чи це просте число. Якщо цього не сталося, він буде телефонувати __next__знову, поки не numberстане простим. Після цього ітератор повертає номер.

Використовуючи ітератор, ми не створюємо список простих чисел у нашій пам’яті. Натомість ми генеруємо наступне просте число кожного разу, коли запитуємо його.

Давайте спробуємо:

primes = Primes(100000000000)
print(primes)
for x in primes: print(x)
---------
235711...

Кожна ітерація Primesоб’єкта викликає __next__генерацію наступного простого числа.

Ітератори можна повторити лише один раз. Якщо ви спробуєте повторити primesповторно, значення не повернеться. Він буде поводитися як порожній список.

Тепер, коли ми знаємо, що таке ітератори та як їх створити, ми перейдемо до генераторів.

Генератори

Нагадаємо, що функції генератора дозволяють нам створювати ітератори простіше.

Генератори вводять yieldзаяву в Python. Це працює приблизно так, returnоскільки повертає значення.

Різниця полягає в тому, що він зберігає стан функції. Наступного разу, коли функцію буде викликано, виконання триватиме з того місця, де вона зупинилася , з тими ж значеннями змінних, що були до отримання.

Якщо ми перетворимо наш Primesітератор на генератор, це буде виглядати так:

def Primes(max): number = 1 while number < max: number += 1 if check_prime(number): yield number
primes = Primes(100000000000)
print(primes)
for x in primes: print(x)
---------
235711...

Тепер це досить пітонічно! Чи можемо ми зробити краще?

Так! Ми можемо використовувати генераторські вирази , представлені у PEP 289.

Це еквівалент генераторів для розуміння списку. Це працює точно так само, як і розуміння списку, але вираз оточений, на ()відміну від [].

Наступний вираз може замінити нашу функцію генератора вище:

primes = (i for i in range(2, 100000000000) if check_prime(i))
print(primes)
for x in primes: print(x)
---------
235711...

У цьому полягає краса генераторів у Python.

У підсумку ...

  • Генератори дозволяють створювати ітератори дуже пітонічно.
  • Ітератори дозволяють ліниву оцінку, лише генеруючи наступний елемент ітерабельного об’єкта на запит. Це корисно для дуже великих наборів даних.
  • Ітератори та генератори можна повторити лише один раз.
  • Функції генератора кращі за ітератори.
  • Вирази генератора кращі за ітератори (лише для простих випадків).

Ви також можете ознайомитися з моїм поясненням того, як я використовував Python, щоб знайти цікавих людей, яким слід слідкувати на Medium.

Щоб отримати додаткові оновлення, слідкуйте за мною у Twitter.