Ответ 1
Разработчики решили, что это ошибка, потому что она может маскировать неясные ошибки. Из-за этого принятие PEP 479 означает, что это уходит.
В Python 3.5, если вы выполняете from __future__ import generator_stop
, а по умолчанию в Python 3.7, пример в вопросе завершится с RuntimeError
. Вы все равно можете достичь такого же эффекта (позволяя nums
не быть предварительно вычисленным) с помощью некоторой магии itertools:
from itertools import tee, islice
def has22(nums):
its = tee(nums, 2)
return any(x == y == 2 for x, y in
zip(its[0], islice(its[1], 1, None)))
Причина, по которой она когда-либо работала, в первую очередь связана с тем, как работают генераторы. Вы можете думать об этом для цикла:
for a in b:
# do stuff
Как (примерно) эквивалентно этому:
b = iter(b)
while True:
try:
a = next(b)
except StopIteration:
break
else:
# do stuff
Теперь все примеры имеют два для объединенных циклов (один в выражении генератора, один в функции, потребляющей его), так что внутренний цикл повторяется один раз, когда внешний цикл выполняет свой вызов next
. Что происходит, когда "# do stuff" во внутреннем цикле raise StopIteration
?
>>> def foo(): raise StopIteration
>>> list(foo() for x in range(10))
[]
Исключение распространяется из внутреннего цикла, поскольку оно не находится в его защите и попадает в внешний цикл. При новом поведении Python перехватит StopIteration
, который собирается размножаться из генератора и заменяет его на RuntimeError
, который не будет улавливаться контуром for.
Это также подразумевает, что этот код выглядит следующим образом:
def a_generator():
yield 5
raise StopIteration
также потерпит неудачу, и поток списка рассылки создает впечатление, что это все равно считается плохим. Правильный способ сделать это:
def a_generator():
yield 5
return
Как вы указали, переписные слова уже ведут себя по-другому:
>>> [foo() for x in range(10)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 1, in foo
StopIteration
Это несколько подробное описание реализации. Проникающие списки не преобразуются в вызов list
с эквивалентным выражением генератора и, по-видимому, делают это приведет к большим штрафам за исполнение, что полномочия, которые считаются запретительными.