Stack Overflow на русском Asked by devnull on December 25, 2020
Столкнулся с очень странным выводом при запуске кода:
def magic():
raise RuntimeError
print("bar")
yield 100
print(magic())
и будет неожиданный для меня ответ:
<generator object gen at 0x7fb3073f0f68>
Вопрос немного обобщенный, хотелось бы понять как это реализовано в python и как понять на уровне кода отличия между обычной функцией и генератором?
потому как тип у генератор не отличается от обычной функции
def no_magic():
pass
type(magic) is type(no_magic)
Схематично работа генератора выглядит так.
Функция возвращает собственно генератор, т.е. специальный объект, который хранит "текущее состояния" выполнения. Это состояние в себя включает место в функции генератора (т.е. команду yeild
), где остановилось предыдущее выполнение.
При вызове next
с параметром возвращенным вызовом magic()
запускается собственно код magic
и когда доходит до yield
, то в этом месте значение, которое передано как аргумент в yield
вернется из next
. Вот пример с разметкой шагов, которые показывают порядок выполнения:
# я изменил функцию, чтобы продемонстрировать работу yield
def magic():
yield 100 # шаг 3
yield 200 # шаг 7
print(1) # шаг 11
yield 300 # шаг 12
print(2) # шаг 16
gen = magic() # шаг 1
a = next(gen) # вызов next - шаг 2, присвоение - шаг 4
print(a) # шаг 5, выведет в консоль 100
a = next(gen) # вызов next - шаг 6, присвоение - шаг 8
print(a) # шаг 9, выведет в консоль 200
a = next(gen) # вызов next - шаг 10, присвоение - шаг 13
print(a) # шаг 14, выведет в консоль 300
next(gen) # вызов шаг 15, исключение StopIteration - шаг 17
# т.к. magic закончила выполнение
Т.е. каждый раз при вызове next(gen)
(а именно так происходит итерация по генератору) выполнение функции возобновляется с предыдущей точки yield
и доходит до следущей, где происходит запоминание этой новой точки и возврат значения в вызывающую функцию.
Что касается типов, то тип magic
и no_magic
тот же - function
. Важно что возвращает функция:
>>> type(magic())
<type 'generator'>
>>> type(no_magic())
<type 'NoneType'>
Сам код no_magic
выполняется непосредственно в точке вызова no_magic()
, у magic()
этого не происходит, вызов отложен или еще по другому говорят, что вычисления делаются "лениво" или "по требованию"(lazy или on demand).
Correct answer by Roman Konoval on December 25, 2020
Генераторы — это функции, которые можно приостанавливать и возобновлять во время их выполнения, при этом они возвращают объект, который можно итерировать. В отличие от списков, они ленивы и поэтому работают с текущим элемент только по запросу. Таким образом, они намного эффективнее используют память при работе с большими наборами данных.
Генераторы возвращают значение через оператор yield
.
А так вы генератор вы создали через magic()
, но вы его не выполнили.
Например так:
def magic():
raise RuntimeError
print("bar")
yield 100
gen = magic()
print(gen)
print(next(gen))
Answered by gil9red on December 25, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP