用 class 模拟收益率
Emulating yield with a class
我有以下 yield
功能:
def yield_numbers(start=1, end=1e6):
num = start
while num <= end:
yield num
num += 1
我想构建一个 class 来复制 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
的工作方式(在最基本的层面上)。如果没有,我在这里错过了什么?再次重申,这不是重新发明轮子或制造坚固的东西,更多的是了解 yield 在概念层面上的工作原理。
虽然您的示例中的行为相同,但这与 yield
的工作方式不同。从根本上说,yield
暂停生成器函数的执行,并在调用 next
时从同一位置恢复;并且生成器函数 开始暂停 ,因此直到第一次调用 next
.
才会执行初始代码
将使用 yield
的代码转换为不使用 yield
的代码是可能的,但并非易事。为了模拟生成器的工作方式,我们需要实例知道它应该假装从哪里 "resume",所以我们称之为 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
我有以下 yield
功能:
def yield_numbers(start=1, end=1e6):
num = start
while num <= end:
yield num
num += 1
我想构建一个 class 来复制 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
的工作方式(在最基本的层面上)。如果没有,我在这里错过了什么?再次重申,这不是重新发明轮子或制造坚固的东西,更多的是了解 yield 在概念层面上的工作原理。
虽然您的示例中的行为相同,但这与 yield
的工作方式不同。从根本上说,yield
暂停生成器函数的执行,并在调用 next
时从同一位置恢复;并且生成器函数 开始暂停 ,因此直到第一次调用 next
.
将使用 yield
的代码转换为不使用 yield
的代码是可能的,但并非易事。为了模拟生成器的工作方式,我们需要实例知道它应该假装从哪里 "resume",所以我们称之为 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