Python生成器:错误仅在评论后可见

时间:2017-03-05 07:36:52

标签: python error-handling generator

我正在尝试使用python代码来模拟* nix系统的'tail'命令。

import sys
def tail(f):
    print 'in tail with ',f
    f.seek(0,2)
    while True:
        line = f.readline()
        if not line:
            time.sleep(0.1)
            continue
        yield line

if(len(sys.argv) >= 2):
    print 'calling tail'
    tail(open(sys.argv[1],'r'))
else:
    print 'Give file path.\n'

我做错了(错过了导入时间模块)。然而,奇怪的是,没有错误被抛出,程序正在静静地退出。 输出(评论前):

$ python tail.py /var/log/dmesg 
calling tail

但是,如果我在使用时间模块的行之后注释行,则会抛出错误。

import sys
def tail(f):
    print 'in tail with ',f
    f.seek(0,2)
    while True:
        line = f.readline()
        if not line:
            time.sleep(0.1)
        #     continue
        # yield line

if(len(sys.argv) >= 2):
    print 'calling tail'
    tail(open(sys.argv[1],'r'))
else:
    print 'Give file path.\n'

输出(评论后)

$ python tail.py /var/log/dmesg 
calling tail
in tail with  <open file '/var/log/dmesg', mode 'r' at 0x7fc8fcf1e5d0>
Traceback (most recent call last):
  File "tail.py", line 14, in <module>
    tail(open(sys.argv[1],'r'))
  File "tail.py", line 8, in tail
    time.sleep(0.1)
NameError: global name 'time' is not defined

任何人都可以解释为什么错误没有在案例一中被抛出(在评论之前)?解释器出现在那条线上时,不应该抛出错误吗?

更正程序:

import sys
import time
def tail(f):
    print 'in tail with ',f
    f.seek(0,2)
    while True:
        line = f.readline()
        if not line:
            time.sleep(0.1)
            continue
        yield line

if(len(sys.argv) >= 2):
    print 'calling tail'
    t = tail(open(sys.argv[1],'r'))
    for i in t:
        print i
else:
    print 'Give file path.\n'

输出:

$ python tail.py hello.txt 
calling tail
in tail with  <open file 'hello.txt', mode 'r' at 0x7fac576b95d0>
hello there 1

hello there 2

hello there 3

感谢您的回复。

2 个答案:

答案 0 :(得分:3)

简答

第一个是实例化一个生成器(但不是将它分配给一个变量),第二个是函数调用。

长答案

这是因为python的动态类型检查,当你有yield语句时,你的函数表现为生成器和这一行 -

tail(open(sys.argv[1],'r'))

表示您实例化生成器未调用函数。当您将此实例分配给某个变量时,您会收到该错误,并为生成器调用next方法实际将其触发,即 -

t = tail(open(sys.argv[1],'r')) # t is a generator here 
t.next()

在你删除yield语句的另一种情况下,它开始表现为正常函数,这意味着 - tail(open(sys.argv[1],'r'))现在是一个函数调用,因此它抛出了一个错误。

我的意思是动态是python不会检查这些错误,直到它到达那个语句,在第一种情况下不是。

答案 1 :(得分:3)

在函数中使用yield,它是一个生成器。生成器函数仅在请求下一个值时执行其包含的代码。简单地调用生成器函数只会创建该生成器对象。如果你这样做而没有对该对象做任何事情,例如循环遍历它,那么什么都不会发生。

删除yield会使函数急切地评估,因此其代码实际执行。

如果您实际迭代生成器,如果/ readline()生成空行,则会产生错误。由于这样的空行只能出现在文件的末尾(看起来像空白行看起来实际上只包含一个换行符),所以无论如何将它置于一个循环中都没有意义。而不是:

while True:
    line = f.readline()
    if not line:
        time.sleep(0.1)
        continue
    yield line

使用此:

for line in f:
    yield line

而不是这个:

if(len(sys.argv) >= 2):
    print 'calling tail'
    tail(open(sys.argv[1],'r'))

您应该实际执行生成器的内容,如下所示:

if(len(sys.argv) >= 2):
    print 'calling tail'
    for line in tail(open(sys.argv[1],'r')):
        print line