【问题标题】:Can I be warned when I used a generator function by accident意外使用生成器功能时会收到警告吗
【发布时间】:2009-07-06 13:38:09
【问题描述】:

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

  1. 为什么在 __someFunc 中产生(在我的一种情况下是偶然的)时,这个函数似乎不是从 __someGenerator 中调用的。另外,在提及语言的这些方面时,我想使用什么术语?
  2. python 解释器可以警告这种情况吗?

以下是我的场景的示例 sn-p。

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

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

【问题讨论】:

  • 正交 Python 建议:前导双下划线是不必要的,只需使用单个前导下划线,Python 编码人员会认识到它不是绅士隐私的界面的一部分。
  • 你怎么不知道你有一个收益声明?其他人是否编辑了您的代码并且您做了一个 SVN 并对更新感到惊讶?导致函数成为生成器而不是普通方法的场景是什么。
  • @S.Lott:复制/粘贴,正如他的评论所提到的。我总是发现复制/粘贴代码比他们解决的问题更令人头疼,但我知道很多人觉得它们很有用。

标签: python function generator language-features


【解决方案1】:

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

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

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

【讨论】:

  • 我终于遇到了 pep 255。它确实介绍了一些细节。 “规范:返回”部分很好地描述了其中的一些。 pep 的其他部分也提到或讨论了 try/catch 和使用 yield 的问题。我确实希望 python 解释器可能会发出警告(可能带有适当的标志);甚至 gcc 也会警告你说的但可能不是故意的。
  • 确实,一些原生编译器可以检查常识问题,偶尔会检查“你做了 X,但你可能是指 Y”,但这种情况很少,而且它们是通常非常具体(例如表达式或 if-test 中的操作顺序。)但更高级别的“我还没有打算从这个函数返回,或者我不想以这种方式返回”仍然是远远超出我所知道的任何编译器。出于好奇,您认为编译器可能会提示您做错了什么?
【解决方案2】:

我会尽量回答你的第一个问题。

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

val = func()

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

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

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

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

【讨论】:

    【解决方案3】:

    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

    【讨论】:

      【解决方案4】:

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

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

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

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

      来自 PEP:

      请注意,return 的意思是“我已经完成了,没有什么有趣的 return",适用于生成器函数和非生成器函数。

      【讨论】:

        【解决方案5】:

        我们这样做。

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-09-27
          • 1970-01-01
          • 2012-02-18
          • 2023-04-04
          • 1970-01-01
          • 2018-01-05
          • 2015-06-19
          • 2020-08-26
          相关资源
          最近更新 更多