Рубрики
Без рубрики

Как использовать генератор и выход в Python

Сегодня мы собираемся говорить о генераторах в Python, как они отличаются от нормальных функций, … Помечено Python, программирование, дата зрения, разработка.

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

Что такое генераторы в Python?

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

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

После этого введение давайте посмотрим некоторые примеры генераторов в действии:

Некоторые используют случаи генераторов

Чтение больших файлов

Общий случай использования генераторов – работать с большими файлами или потоками данных, например, например, файлы CSV. Допустим, нам нужно подсчитать, сколько строк в текстовом файле, наш код может выглядеть что-то вроде:

csv_gen = csv_reader("some_file.txt")
row_count = 0

for row in csv_gen:
    row_count += 1

print(f"Row count is {row_count}")

С нашей CSV_READER Функция реализована следующим образом:

def csv_reader(file_name):
    file = open(file_name)
    result = file.read().split("\n")
    return result

Теперь это очень ясно и просто. Наше CSV_READER Функция будет просто откроет файл в память и прочитать все строки, затем он разделит строки и образует массив с данными файла, поэтому наш код выше будет работать идеально, или поэтому мы думаем.

Если файл содержит несколько тысяч строк, этот код, вероятно, будет работать на любом современном компьютере, однако, если файл достаточно велик, мы начнем иметь некоторую проблему. Эти проблемы могут перейти от машины, начинающей замедляться, к программе, убивая машину до точки, которую нам нужно завершить программу, до максимального использования:

Traceback (most recent call last):
  File "ex1_naive.py", line 22, in 
    main()
  File "ex1_naive.py", line 13, in main
    csv_gen = csv_reader("file.txt")
  File "ex1_naive.py", line 6, in csv_reader
    result = file.read().split("\n")
MemoryError

Мы разбили программу. Файл был слишком большим и не мог быть загружен в память, провоцирующий Python, чтобы поднять исключение и сбой.

Так как мы можем это исправить? Ну … Мы знаем, что с генераторами у нас есть способ построить простые итераторы, так что это должно помочь, давайте теперь посмотрим на CSV_READER Функция, построенная с помощью генератора.

def csv_reader(file_name):
    for row in open(file_name, "r"):
        yield row

Еще очень просто, это выглядит еще более элегантно, чем раньше, но что такое урожай ключевое слово там?

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

Когда функция содержит доходность , Python автоматически (и за кулисами) реализует итератор, применяя все необходимые методы, такие как __iter__ () и __Next__ () Для нас, поэтому нам не нужно беспокоиться о каком-либо из этого.

Возвращаясь к нашему примеру, если теперь мы решим выполнить наш код, мы получим что-то следующим образом:

Row count is 65123455

В зависимости от вашего файла будет другой номер, но что важно, это то, что он работает! Мы были бы ленивы загрузки файла, поэтому мы минимизировали нашу нагрузку на память, и это очень простое элегантное решение.

Но это не конец истории, есть еще простые и интересные способы реализации генераторов, определяя выражение генератора (Также называется A Понимание генератора ), который имеет синтаксис, который выглядит очень похожему на поведение списка.

Давайте посмотрим, как это будет выглядеть

csv_gen = (row for row in open(file_name))

Красиво, не так ли? Просто помните эти основные различия:

  • Использование доходность приведет к объекту генератора
  • Использование вернуть приведет к первому строке только файла.

Генерация бесконечной последовательности

Другим распространенным сценарием для генераторов является бесконечная последовательность. В Python, когда вы используете конечную последовательность, вы можете просто позвонить Диапазон () и оценить его в контексте списка, например:

a = range(5)
print(list(a))
[0, 1, 2, 3, 4]

Мы могли бы сделать то же самое, генерируя Бесконечная последовательность Использование генераторов, как это:

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

И вы можете использовать его, например, чтобы распечатать значения

for i in infinite_sequence():
    print(i, end=" ")

Хотя это будет очень быстро и будет работать «навсегда», поэтому вам придется остановить его вручную, нажав Ctrl + C. Или альтернатива MAC, но вы увидите все числа, напечатанные очень быстро на экране.

Есть и другие способы получения ценностей, например, например, вы можете получить значения один за другим следующим образом:

>> gen = infinite_sequence()
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
....

Больше на урожайность

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

Как мы уже обсуждали, когда мы используем урожай Мы экономите локальное состояние для функции и возвращая выражение значения в функцию вызывающего абонента. Но что мы хочем спасти местное государство? Ну … Вот где это очень интересно. Когда Python доходность Заявление попадает, программа приостанавливает выполнение функции и возвращает текущую стоимость звонящего. Когда функция приостановлена, состояние этой функции сохраняется, это включает в себя данные, такие как любые привязки переменной, указатель инструкции, внутренний стек и любую обработку исключения. Когда генератор снова вызывается, состояние восстановлено, и функция продолжается от последней доходность Заявление о его ударе, как если бы предыдущий доход не был вызван, и функция не была бы приостановлена.

Довольно аккуратно! Давайте посмотрим пример, чтобы понять это лучше

>>> def multiple_yield():
... value = "I'm here for the first time"
... yield value
... value = "My Second time here"
... yield value
...
>>> multi_gen = multiple_yield()
>>> print(next(multi_gen))
I'm here for the first time
>>> print(next(multi_gen))
My Second time here
>>> print(next(multi_gen))
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

Первый раз мы выполнили функцию, указатель выполнения был в начале и, таким образом, мы ударили первым доходность В строке 2, таким образом, утверждение «Я здесь впервые» напечатано на экране. Второй раз Далее () называется указатель выполнения продолжается от линии 3, ударяя второе доходность Заявление о линии 4 и возвращение «Мое второе время здесь», хотя технически было только в этой линии один раз 😛. Когда мы сейчас называем Далее () В течение 3-го времени мы получаем ошибку. Это потому, что генераторы, как и все итераторы, могут быть исчерпаны, и если вы попытаетесь позвонить Далее () После этого вы получите эту ошибку.

Методы продвижения генератора

До сих пор мы охватываем наиболее распространенные использование и сооружения генераторов, но есть еще несколько вещей. Со временем Python добавил несколько дополнительных методов для генераторов, и здесь я хотел бы обсудить следующее:

  • .Отправить ()
  • .бросать ()
  • .близко()

Прежде чем мы войти в детали каждого из этих методов, давайте создадим генератор образца, который мы собираемся использовать в качестве примера. Наш генератор будет генерировать простые номера, так как он реализован как следует следующим образом:

def isPrime(n):
    if n < 2 or n % 1 > 0:
        return False
    elif n == 2 or n == 3:
        return True
    for x in range(2, int(n**0.5) + 1):
        if n % x == 0:
            return False
    return True

def getPrimes():
    value = 0
    while True:
        if isPrime(value):
            yield value
        value += 1

Как использовать .send ()

.send () Позволяет установить значение генератора в любое время. Допустим, вы хотите создать только простые номера от 1000, вот где .send () приходит удобно. Давайте посмотрим на этот пример:

prime_gen = getPrimes()
print(next(prime_gen))
print(prime_gen.send(1000))
print(next(prime_gen))

И когда мы бежим, мы получаем:

2
3
5

мм … Это не пошли так, как планировалось. И проблема находится в функции генератора, которую мы реализовали. Для использования метода отправки нам нужно было бы сделать несколько изменений и заставить его выглядеть так:

def getPrimes():
    value = 0
    while True:
        if isPrime(value):
            i = yield value
            if i is not None:
                value = i
        value += 1

Теперь снова беги

prime_gen = getPrimes()
print(next(prime_gen))
print(prime_gen.send(1000))
print(next(prime_gen))

И получаем:

2
1009
1013

Хороший! Хорошая работа!

Как использовать .throw ()

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

Давайте увидимся в действии:

prime_gen = getPrimes()

for x in prime_gen:
    if x > 10:
        prime_gen.throw(ValueError, "I think it was enough!")
    print(x)

И мы получаем:

2
3
5
7
Traceback (most recent call last):
  File "test.py", line 25, in 
    prime_gen.throw(ValueError, "I think it was enough!")
  File "test.py", line 15, in getPrimes
    i = yield value
ValueError: I think it was enough!

Интересная характеристика в том, что ошибка генерируется из генератора, как видно на трассировке стека.

Как использовать .Close ()

В предыдущем примере мы прекращаем итерацию, поднимая исключение, однако, это не очень элегантно. Лучший способ закончить итерацию, используя .Close () Отказ

prime_gen = getPrimes()

for x in prime_gen:
    if x > 10:
        prime_gen.close()
    print(x)

С выходом:

2
3
5
7
11

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

Заключение

Генераторы, либо используемые как Функции генератора или Разращивания генератора Можно действительно полезно оптимизировать производительность наших приложений Python, особенно в сценариях, когда мы работаем с большими наборами набора данных или файлах. Они также приведут ясность для вашего кода, избегая сложных итераторов внедрения или обрабатывая данные самостоятельно другим способом.

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

Спасибо за прочтение!

Оригинал: “https://dev.to/livecodestream/how-to-use-generator-and-yield-in-python-3eh2”