【问题标题】:Python generator function/object naming conventionPython 生成器函数/对象命名约定
【发布时间】:2026-02-10 00:50:02
【问题描述】:

我在同一个类中实现了一些逻辑进程。 类实例为每个进程获取一个生成器,run() 推进所述生成器。在我的情况下,生成器不会结束。

你会如何在下面的代码中调用 foo_functionfoo_object

class C(threading.Thread):
    def foo_function(self):
        """ generator *function*,
            logical process foo """
        while True:
            # some state checks
            if self.some_attr:
                # side-effects here
                pass
            yield

    def __init__(self):
        # generator *object*
        # i.e. process instance
        self.foo_object = self.foo_function()  # <- here

    def run(self):
        while True:
            next(self.foo_object)
            next(self.another_object)
            if xxx:
                next(self.yet_another_object)

典型流程为discoveryauthenticationwatchdog

如何以合理的方式命名定义生成器的函数和包含生成器对象的属性?

最后只是为了好玩,同名的名字会很疯狂,对吧?

class C:
    def foo(self):
        yield 1; yield 2
    def __init__(self):
        self.foo = self.foo()

c = C()            
type(C.foo) is function
type(c.foo) is generator

【问题讨论】:

  • 小伙子们,你们发表评论怎么样?毕竟它处理martinfowler.com/bliki/TwoHardThings.html :)
  • 正如所写,是的,这很令人困惑。 :) 你有一些具体的细节可能有助于澄清这个非常抽象的例子吗?详细信息: foo() 纯粹是为了副作用而运行? self.maybe_foo = foo_if_some_attr()
  • 你真的把我弄丢了:self.name_me_process_generator = self.name_me_process_function()
  • 好点,我会尝试重新编辑。

标签: python naming-conventions generator naming lightweight-processes


【解决方案1】:

您可以使用您自己设计的某种约定,根据函数名称自动创建包含生成器的成员。例如,在我的约定中,所有生成器都将包含在一个名为:&lt;function_name&gt;_gen 的成员中。

我将函数名称称为它的职责:discoveryauthenticationwatchdog 是好名字。所以你只需要一种方式来自动设置:self.discovery_genself.authentication_genself.watchdog_gen

代码示例:

class C:

    def discovery(self):
        yield 1; yield 2

    # Register all the functions for wich you want to have a generator
    # object.
    REGISTERED_GEN_FUNCTIONS = [discovery]

    def __init__(self):

        for func in self.REGISTERED_GEN_FUNCTIONS:
            name = func.__name__
            # All generators objects will be called <function_name>_gen.
            setattr(self, "{0}_gen".format(name), getattr(self, name)())

a = C()
for i in a.discovery_gen:
    print(i)

输出

>>> 1
>>> 2 

【讨论】:

  • 一次实例化所有生成器的好主意。
  • 这是一个千钧一发的电话。 !Raydel 实际上提供了一个命名约定,@parchment 指出使用匿名对象来完全避免命名约定。我不喜欢这种命名约定,避免是对不同问题的回答。因此,奖品是共享的,一位受访者接受了答案,另一位则获得了赏金。
【解决方案2】:

如果只能在 run 方法中访问生成器,您可以执行以下操作:

class C:
    def a_process(self):
        while True: yield

    def another_process(self):
        while True: yield

    def run(self):
        # Use itertools.izip in python 2
        for _ in zip(a_process(), another_process()):
            pass

在这里,zip 和 for 循环将自动创建和推进生成器。这样,您就无需跟踪它们。

如果您需要在run 方法之外访问生成器,您可以创建一个有序的生成器字典(以防生成器需要按定义的顺序推进):

from collections import OrderedDict

class C:
    # Processes here

    def __init__(self):
        self.generators = OrderedDict()
        for gn in ('a_process', 'another_process'):
            self.generators[gn] = getattr(self, gn)()

    def run(self):
        while True:
            for g in self.generators.values():
                next(g)

【讨论】:

  • 有趣的问题。一个有趣的旁注是我能够重构我的代码以使用iterools.izip;这类似于范围界定(将函数引用保留在类中,将实例引用保留为局部变量)。但是我仍然对原始问题的答案感兴趣。如何命名事物以保持生成器函数和实例明确。
  • 这是一个千钧一发的电话。 @Raydel 实际上提供了一个命名约定, !parchment 指出使用匿名对象来完全避免命名约定。我不喜欢这种命名约定,避免是对不同问题的回答。因此,奖品是共享的,一位受访者接受了答案,而另一位受访者则获得了赏金。
【解决方案3】:

仅解决标题“python 生成器函数命名约定”。

就个人而言,我喜欢将生成器函数命名为“yield_items”。代码读起来很自然,关键字yield表示它输出了一个生成器对象。

def yield_squares(iter_max=10):
    for i in range(iter_max):
        yield i**2

squares = yield_squares()
for square in squares:
    print(square)

【讨论】:

  • 有趣的想法!显式的yield_smth 感觉有点 Python2,不是吗?我的意思是 Python3 返回 mapfilterreversed 的迭代器,因此也许它现在只是标准?