Как работает 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"]
→ Ссылка
Автор решения: Stanislav Volodarskiy

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 попадает в код функции, она перестаёт нормально вызываться. Такие дела.

→ Ссылка