开发者

How to write Python generator function that never yields anything

开发者 https://www.devze.com 2023-03-11 11:19 出处:网络
I want to write a Python generator function that never actually yields anything.Basically it\'s a \"do-nothing\" drop-in that can be used by other code which expects to call a generator (but doesn\'t

I want to write a Python generator function that never actually yields anything. Basically it's a "do-nothing" drop-in that can be used by other code which expects to call a generator (but doesn't always need results from it). So far I have this:

def empty_generator():
    # ... do some stuff, but don't yield anything
    if False:
        yield

Now, this works OK, but I'm wondering if there's a more expressive way to say the same thing, that is, declare a function to be a generator even if it never yields any value. The trick I've employed above is to show Python a yield statement inside my function, even though it is 开发者_运维技巧unreachable.


Another way is

def empty_generator():
    return
    yield

Not really "more expressive", but shorter. :)

Note that iter([]) or simply [] will do as well.


An even shorter solution:

def empty_generator():
  yield from []


For maximum readability and maintainability, I would prioritize a construct which goes at the top of the function. So either

  1. your original if False: yield construct, but hoisted to the very first line, or
  2. a separate decorator which adds generator behavior to a non-generator callable.

(That's assuming you didn't just need a callable which did something and then returned an empty iterable/iterator. If so then you could just use a regular function and return ()/return iter(()) at the end.)

Imagine the reader of your code sees:

def name_fitting_what_the_function_does():
    # We need this function to be an empty generator:
    if False: yield

    # that crucial stuff that this function exists to do

Having this at the top immediately cues in every reader of this function to this detail, which affects the whole function - affects the expectations and interpretations of this function's behavior and usage.

How long is your function body? More than a couple lines? Then as a reader, I will feel righteous fury and condemnation towards the author if I don't get a cue that this function is a generator until the very end, because I will probably have spent significant mental cost weaving a model in my head based on the assumption that this is a regular function - the first yield in a generator should ideally be immediately visible, when you don't even know to look for it.

Also, in a function longer than a few lines, a construct at the very beginning of the function is more trustworthy - I can trust that anyone who has looked at a function has probably seen its first line every time they looked at it. That means a higher chance that if that line was mistaken or broken, someone would have spotted it. That means I can be less vigilant for the possibility that this whole thing is actually broken but being used in a way that makes the breakage non-obvious.

If you're working with people who are sufficiently fluently familiar with the workings of Python, you could even leave off that comment, because to someone who immediately remembers that yield is what makes Python turn a function into a generator, it is obvious that this is the effect, and probably the intent since there is no other reason for correct code to have a non-executed yield.

Alternatively, you could go the decorator route:

@generator_that_yields_nothing
def name_fitting_what_the_function_does():
    # that crucial stuff for which this exists


def generator_that_yields_nothing(wrapped):
    @functools.wraps(wrapped)
    def wrapper_generator():
        if False: yield
        wrapped()
    return wrapper_generator
0

精彩评论

暂无评论...
验证码 换一张
取 消