我能否在意外使用生成器函数时收到警告

2 投票
5 回答
1084 浏览
提问于 2025-04-15 12:42

我在使用生成器函数和类的私有函数时遇到了一些问题。我想知道:

  1. 为什么在__someFunc中使用yield(在我的一个例子中是意外的)时,这个函数似乎没有被__someGenerator调用?我该用什么术语来描述这些语言的特性呢?
  2. Python解释器能否对此类情况发出警告?

下面是我遇到的情况的一个示例代码片段。

class someClass():
    def __init__(self):
        pass

    #Copy and paste mistake where yield ended up in a regular function
    def __someFunc(self):
        print "hello"
        #yield True #if yielding in this function it isn't called

    def __someGenerator (self):
        for i in range(0, 10):
            self.__someFunc()
            yield True
        yield False

    def someMethod(self):
        func = self.__someGenerator()
        while func.next():
            print "next"

sc = someClass()
sc.someMethod()

我在这方面吃了亏,花了一些时间试图弄清楚为什么一个函数根本没有被调用。最后我发现我在一个不想使用yield的函数里用了它。

5 个回答

2

Python 不知道你是想创建一个生成器对象以便后续使用,还是想调用一个函数。不过,Python 不是你唯一可以用来查看代码情况的工具。如果你使用的编辑器或集成开发环境(IDE)支持自定义语法高亮,你可以让它把 yield 这个关键词显示成不同的颜色,甚至加个亮眼的背景,这样能帮助你更快找到错误。比如在 vim 中,你可以这样做:

:syntax keyword Yield yield
:highlight yield ctermbg=yellow guibg=yellow ctermfg=blue guifg=blue

顺便说一下,那些颜色真是太难看了。我建议你选择更好看的颜色。如果你的编辑器或 IDE 不支持这种功能,另一个选择是设置一个自定义规则,在像 pylint 这样的代码检查工具中使用。以下是来自 pylint 源代码包的一个示例:

from pylint.interfaces import IRawChecker
from pylint.checkers import BaseChecker

class MyRawChecker(BaseChecker):
    """check for line continuations with '\' instead of using triple
    quoted string or parenthesis
    """

    __implements__ = IRawChecker

    name = 'custom_raw'
    msgs = {'W9901': ('use \\ for line continuation',
                     ('Used when a \\ is used for a line continuation instead'
                      ' of using triple quoted string or parenthesis.')),
           }
    options = ()

    def process_module(self, stream):
        """process a module

        the module's content is accessible via the stream object
        """
        for (lineno, line) in enumerate(stream):
            if line.rstrip().endswith('\\'):
                self.add_message('W9901', line=lineno)


def register(linter):
    """required method to auto register this checker"""
    linter.register_checker(MyRawChecker(linter))

pylint 的手册可以在这里找到:http://www.logilab.org/card/pylint_manual,而 vim 的语法文档在这里:http://www.vim.org/htmldoc/syntax.html

6

“生成器”其实不是一种语言特性,而是指那些使用“yield”这个关键词的函数。使用“yield”几乎总是合法的。Python 并不能知道你是不是故意要从某个函数中“yield”。

这个 PEP 文档 http://www.python.org/dev/peps/pep-0255/ 讨论了生成器的相关内容,可能会帮助你更好地理解背景。

我理解你的感受,但编译器只能根据你实际写的代码来判断,而不能猜测你“想让它做什么”。

2

我来试着回答你第一个问题。

一个普通的函数,当你这样调用它时:

val = func()

它会执行里面的语句,直到结束或者遇到一个 return 语句。然后,函数的返回值会被赋给 val

但是,如果编译器发现这个函数其实是一个生成器,而不是普通函数(它通过查看函数内部是否有 yield 语句来判断,如果至少有一个 yield,那么就是生成器),那么用上面的方法调用它就会有不同的结果。当你调用 func() 时,函数内部的代码不会被执行,而是会把一个特殊的 <generator> 值赋给 val。接下来,当你第一次调用 val.next() 时,func 的实际语句才会被执行,直到遇到 yieldreturn,这时函数的执行会停止,返回的值会被给出,生成器会等待你下一次调用 val.next()

所以在你的例子中,函数 __someFunc 没有打印 "hello",因为它的语句没有被执行。你没有调用 self.__someFunc().next(),而只是调用了 self.__someFunc()

不幸的是,我很确定没有内置的警告机制来提醒你这种编程错误。

撰写回答