Расскажите девушке об использовании подробного генератора python-S01E16

искусственный интеллект Python
Расскажите девушке об использовании подробного генератора python-S01E16

Добро пожаловать в общедоступный номер: специалист по данным python

【Ключевые моменты, которые нужно увидеть в первую очередь】

1. Использование генераторных функций
2. Использование генераторных выражений
3. Сравнение со списком и оптимизация памяти

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

[Сестра сказала] Тогда сегодня я собираюсь поговорить о генераторах.

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

Одна функция генератора.Внешне это выглядит как функция, но вместо использования оператора return для одновременного возврата всего списка объектов результата используйте оператор yield для возврата одного результата за раз.Другой является выражением генератора.Подобно анализу списка в предыдущем разделе, но с заменой квадратных скобок скобками, они возвращают объект результата, созданный по запросу, а не строя список результатов.

Это «по запросу» означает, что в итеративной среде каждая итерация генерирует объект по требованию, поэтому ни один из двух вышеперечисленных не будет строить весь список за один раз, тем самым экономя место в памяти.

[Сестра сказала] Позвольте мне привести вам несколько примеров.

Что ж, поговорим о генераторных функциях на конкретных примерах.

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

Но функция генератора отличается.После возврата значения через ключевое слово yield она может продолжать работу с того места, где остановилась, поэтому она может генерировать серию значений с течением времени.Они автоматически реализуют протокол итерации и могут появляться в итерационной среде.

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

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

def gen_squares(num):
    for x in range(num):
        yield x ** 2

G = gen_squares(5)
print(G)
print(iter(G))

<generator object gen_squares at 0x0000000002402558>
<generator object gen_squares at 0x0000000002402558>

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

def gen_squares(num):
    for x in range(num):
        yield x ** 2

G = gen_squares(3)
print(G)
print(iter(G))
print(next(G))
print(next(G))
print(next(G))
print(next(G))


<generator object gen_squares at 0x00000000021C2558>
<generator object gen_squares at 0x00000000021C2558>
0
1
4
Traceback (most recent call last):
 File "E:/12homework/12homework.py", line 10, in <module>
print(next(G))
StopIteration

С этой точки зрения его несложно использовать в реальных сценариях использования, таких как циклы for.

def gen_squares(num):
    for x in range(num):
        yield x ** 2

for i in gen_squares(5):
    print(i, end=' ')

0 1 4 9 16 

Перейдем к теме сохранения состояния в генераторных функциях. В каждом цикле функция-генератор создает значение yield и возвращает его вызывающей стороне, циклу for. Затем сохраните внутреннее состояние в yield и выйдите с ожидающим прерыванием. В следующем раунде итеративного вызова выполнение продолжается с места yield, а состояние внутренних переменных функции в предыдущем раунде используется до конца процесса внутреннего цикла.

Для решения этой проблемы см. этот пример:

def gen_squares(num):
    for x in range(num):
        yield x ** 2
        print('x={}'.format(x))

for i in gen_squares(4):
    print('x ** 2={}'.format(i))
    print('--------------')

x ** 2=0
--------------
x=0
x ** 2=1
--------------
x=1
x ** 2=4
--------------
x=2
x ** 2=9
--------------
x=3

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

[Сестра сказала] Давайте поговорим о генераторных выражениях.

Понимание списка уже является хорошим выбором.С точки зрения использования памяти генератор лучше, потому что ему не нужно генерировать весь список объектов сразу.Как преобразовать между ними?

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

Просто взгляните:

print([x ** 2 for x in range(5)])
print((x ** 2 for x in range(5)))

[0, 1, 4, 9, 16]
<generator object <genexpr> at 0x0000000002212558>

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

В то же время он поддерживает протокол итерации, применимый ко всем средам итерации:

Приведу несколько примеров:

for x in (x ** 2 for x in range(5)):
    print(x, end=',')

0,1,4,9,16,

-

print(sum(x ** 2 for x in range(5)))
30

-

print(sorted((x ** 2 for x in range(5)), reverse=True))
[16, 9, 4, 1, 0]

-

print(list(x ** 2 for x in range(5)))
[0, 1, 4, 9, 16]

Описание: Выражения генератора — это оптимизация памяти.Им не нужно сразу строить весь список результатов, как при включении списка в квадратные скобки. Они могут работать немного медленнее, чем генераторы списков, поэтому они оптимальны для операций с очень большими наборами результатов.

[Сестра сказала] Это резюмируется в одном предложении: понимание списка является самым быстрым, выражение генератора является самым компактным, и скорость в порядке.

Чтобы добавить примечание:

Разбор набора эквивалентен передаче объектов-генераторов в такие функции, как список, набор, дикт и т. д., в качестве параметров построения.

set(f(x) for x in S if P(x))
{f(x) for x in S if P(x)}

{key:val for (key, val) in zip(keys, vals)}
dict(zip(keys, vals))

{x:f(x) for x in items}
dict((x, f(x)) for x in items)

QR-код официального аккаунта: специалист по данным python: