Python生成器函数/对象的命名惯例

7

我有几个逻辑进程实现在同一个类中。 一个类实例为每个进程获取一个生成器,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)

典型的过程包括“发现(discovery)”,“认证(authentication)”,“看门狗(watchdog)”等。

我应该如何为定义生成器(generator)和包含生成器对象的属性(attribute)命名一个合理的函数?

最后,同样的名称会让人感到疯狂,对吧?

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

1
伙计们,你们怎么样留下评论,说明你认为是基于个人观点的?毕竟这与 http://martinfowler.com/bliki/TwoHardThings.html 有关 :) - Dima Tisnek
1
就照字面意思来看,这确实很令人困惑。 :) 您有一些具体的细节可以帮助澄清这个非常抽象的例子吗?详细信息:foo()只为副作用而运行?self.maybe_foo = foo_if_some_attr() - spazm
1
你在这行代码上让我有点迷糊了:self.name_me_process_generator = self.name_me_process_function() - spazm
好的,我会尝试重新编辑。 - Dima Tisnek
3个回答

4

您可以根据自己设计的某种约定,使包含生成器的成员从函数名称自动创建。例如,在我的约定中,所有生成器将由名为:<function_name>_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 

一次性实例化所有生成器的想法很酷。 - Dima Tisnek
这真是一次惊险的经历。!Raydel提供了一个命名约定,而@parchment则指出使用匿名对象可以避免使用命名约定。我不喜欢这个命名约定,而避免使用则是回答另一个问题的答案。因此,奖励将分享给一个回答者的被接受的答案和另一个回答者的赏金。 - Dima Tisnek
在运行时创建方法别名看起来像是一个非常糟糕的想法,因为IDE将无法找到该方法的定义,而且代码检查工具也会发出警告。 相反,只需明确地命名它们为 xxx_gen() - HenriTel

3
如果生成器只在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; 这在某种程度上类似于作用域(将函数引用保留在类中,将实例引用保留为本地变量)。然而,我仍然对原始问题的答案感兴趣。如何命名以使生成器函数和实例不会产生歧义。 - Dima Tisnek
这是一次惊险的时刻。@Raydel实际上提供了一个命名约定,而!parchment则指出使用匿名对象来避免任何命名约定。我不喜欢这个命名约定,而回避是另一个问题的答案。因此,奖品将分享给一个回答者的被接受的答案和另一个回答者的赏金。 - Dima Tisnek

3

仅针对标题“python生成器函数命名约定”的内容。

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

最初的回答:

对于“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)

2
有趣的想法!但是显式的 yield_smth 感觉有点像 Python2,不是吗?我的意思是 Python3 为 mapfilterreversed 返回迭代器,因此现在可能已经成为标准了。 - Dima Tisnek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接