简体   繁体   English

Python生成器:错误仅在注释后可见

[英]Python generators: Errors visible only after commenting

I was trying following python code to simulate 'tail' command of *nix systems. 我正在尝试遵循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'

I did an error (missed importing time module). 我犯了一个错误(缺少导入时间模块)。 However, what's odd is that no error was getting thrown and program was quitting silently. 但是,奇怪的是没有错误被抛出并且程序正在悄悄地退出。 Output (before commenting): 输出(评论前):

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

However if I comment lines following the one using the time module, the error does get thrown. 但是,如果我在使用时间模块的注释行之后注释行,则会引发错误。

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'

Output (after commenting) 输出(评论后)

$ 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

Can anyone please explain why the error was not getting thrown in case one (before commenting)? 任何人都可以解释为什么在第一种情况下未引发错误(在评论之前)吗? Shouldn't a error be thrown as soon as interpreter comes on that line? 解释器上线后,是否应该立即抛出错误?

Corrected program: 更正的程序:

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'

Output: 输出:

$ 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

Thanks for the responses. 感谢您的答复。

Short Answer 简短答案

First one is instantiating a generator (but not assigning it to a variable) and second one is a function call. 第一个是实例化生成器(但不将其分配给变量),第二个是函数调用。


Long Answer 长答案

This is because of dynamic type checking of python, when you have the yield statement, your function behaves as a generator and this line - 这是由于python的动态类型检查,当您具有yield语句时,您的函数就相当于生成器,并且此行-

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

means that you are instantiating the generator not calling a function . 表示您正在实例化生成器而不调用函数 You'll get that error when you assign this instance to some variable and call the next method for generator which actually fires it up ie - 当您将此实例分配给某个变量并为生成器调用next 实际触发它的方法时,就会得到该错误,即-

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

The other case in which you removed the yield statement, it started behaving as a normal function which means - tail(open(sys.argv[1],'r')) is a function call now, and hence it threw an error. 在另一种删除yield语句的情况下,它开始表现为正常功能,这意味着tail(open(sys.argv[1],'r'))现在是函数调用,因此引发错误。

What I meant by dynamic is python doesn't check these kind of errors until it reaches that statement, which in first case wasn't. 我所说的动态是python在到达该语句之前不会检查这些类型的错误,在第一种情况下不是这样。

With yield in the function, it is a generator. 在函数yield中,它是一个生成器。 Generator functions only execute their contained code when the next value is requested. 生成器函数仅在请求下一个值时才执行其包含的代码。 Simply calling a generator function merely creates that generator object. 简单地调用生成器函数只会创建该生成器对象。 If you do so without doing anything with that object, such as looping through it, nothing will happen. 如果您不对该对象执行任何操作(例如遍历对象),则不会发生任何事情。

Removing the yield makes the function evaluate eagerly, so its code is actually executed. 删除yield使函数急切地求值,因此实际上执行其代码。

If you actually iterated over the generator, it would produce an error if/when readline() produced an empty line. 如果您实际上遍历了生成器,则当/当readline()生成空行时,它将生成一个错误。 Since such an empty line can only occur at the end of a file (what look like blank lines actually contain a single linefeed character), putting it in a loop doesn't make sense anyway. 由于这样的空行只能出现在文件的末尾(看起来像空行实际上包含单个换行符),因此将其置于循环中毫无意义。 Instead of this: 代替这个:

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

Use this: 用这个:

for line in f:
    yield line

And instead of this: 而不是这样:

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

You should actually execute the generator's contents, with something like this: 您实际上应该执行生成器的内容,如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM