如何编写永不产生任何结果的 Python 生成器函数

发布于 2024-11-14 07:08:06 字数 344 浏览 3 评论 0原文

我想编写一个实际上不会产生任何结果的 Python 生成器函数。基本上,它是一个“什么都不做”的插件,可以被其他期望调用生成器的代码使用(但并不总是需要从中得到结果)。到目前为止我有这样的:

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

现在,这工作正常,但我想知道是否有一种更具表现力的方式来表达同样的事情,即声明一个函数为生成器,即使它永远不会产生任何值。我上面使用的技巧是在我的函数中向 Python 显示一个yield 语句,即使它无法访问。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

春风十里 2024-11-21 07:08:06

一个更短的解决方案:

def empty_generator():
  yield from []

An even shorter solution:

def empty_generator():
  yield from []
—━☆沉默づ 2024-11-21 07:08:06

另一种方式

def empty_generator():
    return
    yield

并不是真正“更具表现力”,而是更短。 :)

请注意,iter([]) 或简单的 [] 也可以。

Another way is

def empty_generator():
    return
    yield

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

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

血之狂魔 2024-11-21 07:08:06

为了获得最大的可读性和可维护性,我会优先考虑位于函数顶部的构造。因此,要么是

  1. 原始的 if False:yield 构造,但提升到第一行,要么是
  2. 一个单独的装饰器,它将生成器行为添加到非生成器可调用对象中。

(假设您不仅仅需要一个执行某些操作然后返回一个空的迭代器/迭代器的可调用函数。如果是这样,那么您可以只使用常规函数和 return ()/return iter (()) 位于末尾。)

想象一下代码的读者会看到:

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

将 this 放在顶部会立即提示该函数的每个读者了解此细节,这会影响整个函数 - 影响期望和该函数的行为和用法的解释。

你的函数体有多长?超过几行?那么作为一名读者,如果我直到最后都没有得到这个函数是一个生成器的提示,我会对作者感到正义的愤怒和谴责,因为我可能会花费大量的精神成本在我的头脑中编织一个基于模型的模型。假设这是一个常规函数 - 理想情况下,生成器中的第一个 yield 应该立即可见,当您甚至不知道寻找它时

另外,在一个超过几行的函数中,函数最开始的构造更值得信赖 - 我可以相信任何看过函数的人每次都可能看到它的第一行他们看着它。这意味着如果该线错误或损坏,有人会发现它的可能性更高。这意味着我可以不太警惕整个东西实际上已经损坏但以一种使损坏不明显的方式使用的可能性。

如果您正在与非常熟悉 Python 工作原理的人一起工作,您甚至可以省略该注释,因为对于那些立即记住 yield 的人来说,yield 是 Python 将函数变成函数的原因。生成器,很明显这是效果,并且可能是意图,因为没有其他原因使正确的代码具有未执行的yield.

或者,您可以走装饰器路线:

@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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文