Эмуляция доходности с классом - PullRequest
2 голосов
/ 28 февраля 2020

У меня есть следующая yield функция:

def yield_numbers(start=1, end=1e6):
    num = start
    while num <= end:
        yield num
        num += 1

Я хотел бы создать класс для репликации наиболее базовой c функциональности выражения yield, чтобы понять, как оно работает немного Больше. Вот что у меня есть:

class YieldNumbers:
    def __init__(self, start=1, end=1e6):
        self.start = int(start)
        self.end = int(end)
        self._current_val = None
        self._closed = False
    def __iter__(self):
        if self._closed: raise StopIteration
        return self
    def close(self):
        self._closed = True
    def send(self, value):
        return self.__next__(value)
    def throw(self, exc_type):
        assert isinstance(exc_type, Exception)
        return self.__next__(exc_type=exc_type)
    def __next__(self, value=None, exc_type=None):
        if self._closed: raise StopIteration
        if exc_type: raise exc_type
        self._current_val = value if value else self.start if not self._current_val else self._current_val + 1
        if self._current_val > self.end: self._closed=True; raise StopIteration
        return self._current_val

И как его использовать:

for i in YieldNumbers(start=1,end=3):
    print (i)
1
2 
3
>>> y=YieldNumbers()
>>> next(y)
1
>>> y.close()
>>> next(y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in __next__
StopIteration

Это похоже на то, как работает yield (на самом базовом уровне c). Если нет, то что мне здесь не хватает? Опять же, дело не в том, чтобы заново изобрести колесо или сделать что-то надежное, а в том, чтобы понять, как урожайность работает на концептуальном уровне.

1 Ответ

3 голосов
/ 28 февраля 2020

Хотя поведение в вашем примере такое же, это не похоже на то, как работает yield. По сути, yield приостанавливает выполнение функции генератора и возобновляется с того же места, когда вызывается next; и функция генератора начинает приостанавливаться , поэтому исходный код не выполняется до первого вызова next.

Возможно, но нетривиально преобразовать код с помощью yield в код, который не использует yield. Чтобы смоделировать работу генератора, нам нужно, чтобы экземпляр знал, откуда он должен претендовать на «возобновление», поэтому назовем это resume_point. Я не беспокоился о некоторых деталях, таких как отправка данных обратно в генератор, обработка исключений или закрытие; но вы поняли.

class Example:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.resume_point = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.resume_point == 0:
            # simulates the beginning of your generator function
            self.num = self.start
            # simulates checking the `while` loop condition
            if self.num <= self.end:
                # next time we should continue from the `yield`
                self.resume_point = 1
                # `return` simulates pausing the generator function
                return self.num
            # simulates the end of your generator function
            self.resume_point = 2
            raise StopIteration
        elif self.resume_point == 1:
            # simulates continuing from the `yield` in your generator function
            self.num += 1
            # simulates checking the `while` loop condition
            if self.num <= self.end:
                # redundant, but it shows what happens
                self.resume_point = 1
                # `return` simulates pausing the generator function
                return self.num
            # simulates the end of your generator function
            self.resume_point = 2
            raise StopIteration
        elif self.resume_point == 2:
            # simulates resuming from after your generator function returned
            self.resume_point = 2
            raise StopIteration
...