Връщане в генератор заедно с добив в Python 3.3

В Python 2 имаше грешка, когато return беше заедно с yield в дефиницията на функцията. Но за този код в Python 3.3

def f():
  return 3
  yield 2

x = f()
print(x.__next__())

няма грешка, че return се използва във функция с добив. Въпреки това, когато се извика функцията __next__, тогава се хвърля изключение StopIteration. Защо няма просто върната стойност 3? Това връщане някак ли е игнорирано?


person scdmb    schedule 27.05.2013    source източник


Отговори (2)


Това е нова функция в Python 3.3 (както отбелязва коментар, тя дори не работи в 3.2). Подобно на return в генератор отдавна е еквивалентен на raise StopIteration(), return <something> в генератор сега е еквивалентен на raise StopIteration(<something>). Поради тази причина изключението, което виждате, трябва да бъде отпечатано като StopIteration: 3 и стойността е достъпна чрез атрибута value на обекта за изключение. Ако генераторът е делегиран да използва (също нов) синтаксис yield from, това е резултатът. Вижте PEP 380 за подробности.

def f():
    return 1
    yield 2

def g():
    x = yield from f()
    print(x)

# g is still a generator so we need to iterate to run it:
for _ in g():
    pass

Това отпечатва 1, но не и 2.

person Community    schedule 27.05.2013

Върнатата стойност не се игнорира, но генераторите само получават стойности, a return просто завършва генератора, в този случай рано. Напредването на генератора никога не достига до оператора yield в този случай.

Всеки път, когато итератор достигне „края“ на стойностите, които трябва да бъдат получени, StopIteration трябва да бъде повдигнато. Генераторите не са изключение. От Python 3.3 обаче всеки израз return става стойността на изключението:

>>> def gen():
...     return 3
...     yield 2
... 
>>> try:
...     next(gen())
... except StopIteration as ex:
...     e = ex
... 
>>> e
StopIteration(3,)
>>> e.value
3

Използвайте функцията next() за напредване на итераторите, вместо да извиквате .__next__() директно:

print(next(x))
person Martijn Pieters    schedule 27.05.2013
comment
return със стойност не се игнорира, това е или синтактична грешка (в 3.2 и по-ранни) или не се игнорира (в 3.3 и по-нови). - person ; 28.05.2013
comment
@delnan: Стойността не се използва; не можете да използвате return вместо yield. Функцията завършва, така че StopIteration се повдига и върнатата стойност в най-добрия случай се отхвърля. OP знае преди 3.3 връщане със стойност е синтактична грешка. - person Martijn Pieters; 28.05.2013
comment
return наистина не е заместител на yield, но стойността не е напразна. Той е достъпен като атрибут на обекта за изключение и yield from дава удобен достъп до него. - person ; 28.05.2013
comment
@delnan: Добре, току-що го намерих: атрибутът стойност на повдигнатия екземпляр StopIteration [...] може да бъде зададен изрично [...] или автоматично чрез връщане на стойност от подгенератора ; - person Martijn Pieters; 28.05.2013