2

我正在使用类的生成器函数和私有函数。我想知道

  1. 为什么在 __someFunc 中产生(在我的一种情况下是偶然的)时,这个函数似乎不是从 __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()

我对此感到很恼火,并花了一些时间试图弄清楚为什么一个函数没有被调用。我终于发现我正在屈服于我不想参与的功能。

4

5 回答 5

6

“生成器”与其说是一种语言特性,不如说是“生成”函数的名称。让步几乎总是合法的。Python 没有任何方法可以知道您不是“故意”从某个函数中产生的。

这个 PEP http://www.python.org/dev/peps/pep-0255/讨论了生成器,可以帮助你更好地理解背景。

我同情你的经历,但编译器无法弄清楚你“想让他们做什么”,只有你实际告诉他们做什么。

于 2009-07-06T13:43:41.453 回答
2

我将尝试回答您的第一个问题。

一个常规函数,当这样调用时:

val = func()

执行其内部语句,直到它结束或return到达一个语句。然后将函数的返回值赋给val

如果编译器识别出该函数实际上是一个生成器而不是常规函数(它通过yield在函数内部查找语句来做到这一点——如果至少有一个,它就是一个生成器),调用它时的场景与上面相同有不同的后果。调用func()时,不会执行函数内部的任何代码,并且会为其分配一个特殊<generator>val。然后,第一次调用val.next()时,实际的语句func将被执行,直到遇到yieldorreturn时,函数的执行停止,返回值 yielded 并且生成器等待另一个调用val.next()

这就是为什么在你的例子中,函数__someFunc没有打印“hello”——它的语句没有被执行,因为你没有调用self.__someFunc().next(),而只是self.__someFunc()

不幸的是,我很确定没有像您这样的编程错误的内置警告机制。

于 2009-07-06T14:21:41.323 回答
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

于 2009-07-06T14:37:16.307 回答
1

因为该return关键字适用于生成器函数和常规函数,所以您无法检查任何内容(正如@Christopher 所提到的)。生成器中的return关键字表示应该引发 StopIteration 异常。

如果您尝试return使用生成器中的值(这没有意义,因为return只是意味着“停止迭代”),编译器将在编译时抱怨——这可能会捕获一些复制和粘贴错误:

>>> def foo():
...     yield 12
...     return 15
... 
  File "<stdin>", line 3
SyntaxError: 'return' with argument inside generator

我个人只是建议不要复制和粘贴编程。:-)

来自 PEP:

请注意,对于生成器函数和非生成器函数,return 意味着“我已经完成了,并且没有什么有趣的要返回”。

于 2009-07-06T14:02:40.227 回答
0

我们这样做。

生成器的名称中带有“generate”或“gen”。它将在正文中有一个 yield 语句。很容易目视检查,因为没有任何方法超过 20 行代码。

其他方法的名称中没有“gen”。

此外,我们不会__在任何情况下都使用(双下划线)名称。32,000 行代码。非__姓名。

“生成器与非生成器”方法函数完全是一个设计问题。程序员“打算”发生什么。编译器无法轻易验证您的意图,它只能验证您实际输入的内容。

于 2009-07-06T14:50:16.660 回答