Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purpose of python yield when not used in iterator

I've inherited some fairly buggy code from another project. One of the functions is a callback(draw_ui method) from a library that has a yield statement in it. I'm wondering what the purpose of having a yield in python is if your not using it in a iterator context to return a value. What possible benefit could it have?

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
like image 286
Jennifer Heckman Avatar asked May 15 '26 14:05

Jennifer Heckman


1 Answers

When a function has empty yield statement, the function will just return None for the first iteration, so you can say that the function acts as generator that can be iterated only once and yields None value:

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

That's what an empty yield does. But when a function has empty yield in between two block of code, it will execute the codes before yield for the first iteration, and the codes after yield will be executed on second iteration:

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

So, it somehow allows you to pause the execution of a function in the middle, however, it throws StopIteration Exception for the second iteration because the function doesn't actually yield anything on the second iteration, to avoid this, you can pass a default value to next function:

Looking at your code, your function is also doing the same thing

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

So, while calling funciton draw_ui, if control goes to else block, then line outside the else block, i.e. imgui.end() is not called until the second iteration.

This type of implementation is generally done to be used in ContextManager and you can relate to following code snippet copied from contextlib.contextmanager documentation

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
like image 56
ThePyGuy Avatar answered May 18 '26 02:05

ThePyGuy