我偶然使用发电机功能时可以收到警告

时间:2009-07-06 13:38:09

标签: python function generator language-features

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

  1. 为什么在__someFunc中屈服(在我的一个案例中是偶然的)这个函数似乎不会从__someGenerator中调用。在提到语言的这些方面时,我想要使用的术语是什么?
  2. python解释器可以警告这种情况吗?
  3. 以下是我的方案的示例片段。

    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()
    

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

5 个答案:

答案 0 :(得分:6)

“生成器”不是语言特征,而是“屈服”的函数的名称。屈服几乎总是合法的。对于Python来说,没有任何方法可以知道你并不是“意味着”从某个函数中获得收益。

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

我很同情你的经历,但是编制者无法弄清楚你“对他们来说意味着什么”,只是你实际告诉他们要做的事情。

答案 1 :(得分:2)

我会尝试回答你的第一个问题。

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

val = func()

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

如果编译器认识到函数实际上是一个生成器而不是常规函数(它通过在函数内部查找yield语句来实现 - 如果至少有一个,它是一个生成器),那么场景当以与上述相同的方式调用它时会产生不同的后果。在调用func()时,不会执行函数内的代码,并且会为<generator>分配一个特殊的val值。然后,第一次调用val.next()时,func的实际语句将被执行,直到遇到yieldreturn为止,函数的执行停止,返回值yielding,生成器等待另一个val.next()的调用。

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

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

答案 2 :(得分:2)

Python不知道您是否要为以后的迭代创建生成器对象或调用函数。但是python不是你查看代码发生情况的唯一工具。如果您使用的是允许自定义语法高亮显示的编辑器或IDE,您可以告诉它为yield关键字提供不同的颜色,甚至是明亮的背景,这将有助于您更快地找到错误,至少。例如,在vim中,您可能会这样做:

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

顺便说一句,这些都是可怕的颜色。我建议选择更好的东西。如果您的编辑器或IDE不合作,另一个选项是在像pylint这样的代码检查器中设置自定义规则。 pylint源码tarball的一个例子:

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

答案 3 :(得分:1)

因为return关键字适用于生成器函数和常规函数,所以没有什么可以检查的(如@Christopher所提到的)。生成器中的return关键字表示应该引发StopIteration异常。

如果你尝试return来自生成器中的值(这没有意义,因为return只是意味着“停止迭代”),编译器会在编译时抱怨 - - 这可能会发现一些复制和粘贴错误:

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

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

来自PEP:

  

请注意,返回意味着“我已经完成了,并没有任何有趣的东西   返回“,用于发电机功能和非发电机功能。

答案 4 :(得分:0)

我们这样做。

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

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

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

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