Как работает yield python
у меня допустим есть следущий код:
def channel(*names):
for i in names:
yield channels[i]
print(channel('ru', 'rul'))
я планировал что channel вернёт мне [channel['ru'], channel['rul']], но возвращает объект генератор.
Объясните, как мне сразу получить список генератора?
Я мог не использовать генератор, а просто переменную и .append(), но узнал про генератор, который можно уместить в одну строку.
Ответы (2 шт):
yield создаёт функцию генератор (он же итератор в других языках).
Генератор имеет текущий элемент и функцию, для перехода на следующий элемент. По нему (например) можно проходиться с использованием for, распаковывать и выполнять другие действия, присущие любому другому итератору:
def channel(*names):
for i in names:
yield i
for i in channel("a", "b"):
print(i);
# или
print(*channel("a", "b")) # -> a b
Вы можете воспользоваться декораторами, если хотите что бы ваш результат автоматически оборачивался в список (чего я крайне не рекомендую):
def genList(fn):
return lambda *args, **kwargs: list(fn(*args, **kwargs))
@genList
def channel(*names):
for i in names:
yield i
print(channel("a", "b")) # -> ["a", "b"]
yield в теле функции принципиально меняет её поведение:
def f():
print('in f')
return 42
def g():
print('in g')
yield 42
print('before f()')
f() # печатает 'in f'
print('after f()')
print('before g()')
it = g() # ничего не печатает
print('after g()')
it.__next__() # печатает 'in g'
Если в теле функции есть yield и вы вызываете эту функцию, она не вызывается и её тело не исполняется.
Вместо этого создаётся объект "генератор", который хранит функцию и значения её аргументов. Чтобы запустить исполнение тела функции, надо у "генератора" вызвать метод __next__. В этот момент тело функции начнёт исполняться и остановится после yield. Чтобы продолжить исполнение, снова нужно вызвать __next__. Тело функции продолжит выполнение до следующего yield.
В нормальном коде вызовов __next__ не бывает. Все вызывают глобальную функцию next или подставляют "генератор" в цикл (for v in g():) или передают генератор в функцию, которая из него извлекает значения (list, tuple).
Так что вам нужно что-то вроде print(list(channel('ru', 'rul'))).
P.S. Такое поведение yield приводит к удивительным ошибкам. Была у вас функция на два экрана. В один прекрасный день вы её вызываете, а она не вызывается, отладчик внутрь не заходит. Почему? Неудачный merge склеил её тело с телом генератора ниже, в котором был yield. А как только yield попадает в код функции, она перестаёт нормально вызываться. Такие дела.