我正在使用生成器函数和类的私有函数。我想知道
以下是我的方案的示例片段。
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()
我对此感到厌烦,并花了一些时间试图弄清楚为什么函数没有被调用。我终于发现我正在屈服于我不想进入的功能。
答案 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
的实际语句将被执行,直到遇到yield
或return
为止,函数的执行停止,返回值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行代码。非__
个名称。
“发电机与非发电机”方法功能完全是一个设计问题。程序员“打算”发生了什么。编译器无法轻松验证您的意图,它只能验证您实际输入的内容。