3

我从另一个项目继承了一些相当错误的代码。其中一个函数是来自库的回调(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
4

1 回答 1

1

当一个函数有空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

因此,它以某种方式允许您在中间暂停函数的执行,但是,它会StopIteration为第二次迭代抛出异常,因为该函数实际上在第二次迭代中没有yield任何作用,为避免这种情况,您可以将默认值传递给next功能:

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

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()  # 

因此,在调用 funcitondraw_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
于 2021-08-13T17:48:45.163 回答