python yield在不用于迭代器时的目的



我从另一个项目继承了一些相当有bug的代码。其中一个函数是来自库的回调函数(draw_ui方法),其中包含yield语句。我想知道在python中使用yield的目的是什么,如果你不在迭代器上下文中使用它来返回一个值。它可能有什么好处?

def draw_ui(self, graphics):
self._reset_components()
imgui.set_next_window_size(200, 200, imgui.ONCE)
if imgui.begin("Entity"):
if not self._selected:
imgui.text("No entity selected")
else:
imgui.text(self._selected.name)
yield
imgui.end()  # end entity window

当函数的yield语句为空时,该函数将在第一次迭代时只返回None,因此您可以说该函数充当生成器,只能迭代一次并产生None值:

def foo():
yield
>>> f = foo()
>>> print(next(f))
None
>>> print(next(f))
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration

这就是空yield的作用。但是当一个函数在两个代码块之间有空的yield时,它将在第一次迭代中执行yield之前的代码,在第二次迭代中执行yield之后的代码:

def foo():
print('--statement before yield--')
yield
print('--statement after yield--')
>>> f = foo()
>>> next(f)
--statement before yield--
>>> next(f)
--statement after yield--
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration

因此,它以某种方式允许您在中间暂停函数的执行,然而,它会在第二次迭代时抛出StopIterationException,因为函数在第二次迭代时实际上没有yield任何内容,为了避免这种情况,您可以传递一个默认值给nextfunction:

看看你的代码,你的函数也在做同样的事情

def draw_ui(self, graphics):
self._reset_components()
imgui.set_next_window_size(200, 200, imgui.ONCE)
if imgui.begin("Entity"):
if not self._selected:
imgui.text("No entity selected")
else:
imgui.text(self._selected.name)
yield  #<--------------
imgui.end()  # 

因此,当调用函数draw_ui时,如果控制转到else块,则在else块之外的行,即imgui.end()直到第二次迭代才被调用。

这种类型的实现通常在ContextManager中使用,您可以将其与以下从contextlib复制的代码片段联系起来。contextmanager文档

from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource)
>>> with managed_resource(timeout=3600) as resource:
...     # Resource is released at the end of this block,
...     # even if code in the block raises an exception

最新更新