Возврат и остановка изменения в пользовательских генераторах Python - PullRequest
0 голосов
/ 17 мая 2019

Ознакомление с некоторыми новыми взглядами на стиль в Python и переход к переходу с StopIteration на return в генераторах.Мой главный вопрос, как именно это должно работать в собственном генераторе.У меня есть класс, где я переписываю метод __next__ напрямую, так как есть некоторая логика, которую я должен добавить для генерации трека.

Ниже довольно близко к тому, что я делаю, естьвсе критические элементы.Обратите внимание, что я на самом деле не просто создаю генератор, который реплицирует список, я просто делаю это минимальным примером.

class Test():
    items = <list>

    def __init__(self):
        self.index = 0

    def __next__(self):
        if self.index >= len(self.items):
            raise StopIteration
        value = self.items[self.index]
        self.index += 1
        return value

    def reset(self):
        self.index = 0

Так что в таком случае я перебираю список, пока он не исчерпан, а затемнисходящие вызовы решат, сбросить ли генератор или продолжить после того, как он исчерпан.Тем не менее, как я могу включить что-то подобное без использования StopIteration, поскольку это устарело?Стандартный совет по использованию return здесь для поднятия StopIteration, похоже, не применим, и я бы действительно не хотел менять нисходящий код для проверки генератора, дающего Nones

Так что же ядолжен делать здесь?StopIteration исключения все еще допустимы в __next__?

Ответы [ 2 ]

3 голосов
/ 17 мая 2019

StopIteration означает не устаревшим, вы просто неправильно поняли, что такое генераторы.На самом деле у вас нет генератора, у вас есть итератор .Генераторы - это просто функции, которые используют yield для создания итератора.

Вы создаете свою собственную базовую реализацию итератора без использования генераторов.Итераторы поднимают StopIterator с __next__, когда они сделаны.Ваш код правильно делает это здесь.

Из Типов генераторов раздел документации по модели данных Python:

Генератор Python's обеспечивают удобный способ реализации протокола итератора.Если метод контейнерного объекта __iter__() реализован как генератор, он автоматически вернет объект итератора (технически объект генератора), предоставляющий __iter__() и __next__() методов.Более подробную информацию о генераторах можно найти в документации по выражению yield.

И в той же документации, в разделе Типы итераторов в разделе :

iterator.__next__()
Вернуть следующий предмет из контейнера.Если других элементов больше нет, выведите исключение StopIteration.

Существует устаревшее место, но это касается только использования StopIteration в функциях генератора.См. PEP 479 - Изменение обработки StopIteration внутри генераторов :

Этот PEP предлагает изменить генераторы: когда StopIteration поднимается внутри генератора, он заменяется на RuntimeError.(Точнее, это происходит, когда исключение вот-вот выйдет за пределы стекового кадра генератора.) Поскольку изменение обратно несовместимо, функция изначально вводится с помощью оператора __future__.

Внутригенератор, используя return, вызывает исключение StopIteration.Повышение StopIteration вручную может фактически создать скрытые ошибки, потому что любой потребитель получающегося в результате итератора не может различить правильное использование исключения и неправильное, случайное StopIteration исключения, исходящие изтвой генератор.Это затрудняет отладку таких проблем, поэтому их использование внутри функции генератора изменится в будущей версии Python.

Примечание: ваша реализация требует iterator.__iter__() метод , которыйпросто возвращает self.

0 голосов
/ 17 мая 2019

Если вы хотите сбросить обернутый генератор, один из возможных вариантов - сохранить его данные в другом месте:

class Gen:
   def __init__(self):
      self.items = []
   def restart(self):
      for x in self.items:
         yield x

g = Gen()
for x in g.restart():
   pass
for x in g.restart():
   pass
...