如何在yield调用函数中防止或捕获StopIteration异常?

我们的一个库中的生成器返回函数(即其中包含yield语句的函数)由于未处理的StopIteration异常而导致某些测试失败为了方便起见,在本文中,我将此功能称为buggy

我还没有找到buggy防止异常的方法(不影响函数的正常运行)。同样,我也没有找到在中捕获异常(带有try/ except)的方法buggy

使用的客户端代码buggy可以捕获此异常,但这为时已晚,因为该buggy函数具有正确处理导致此异常的条件所必需的信息。)

我正在使用的实际代码和测试用例过于复杂,无法在此处发布,因此我创建了一个非常简单但又非常人为的玩具示例来说明问题。

一,具有以下buggy功能的模块

# mymod.py

import csv  # essential!

def buggy(csvfile):
    with open(csvfile) as stream:

        reader = csv.reader(stream)

        # how to test *here* if either stream is at its end?

        for row in reader:
            yield row

如评论所述,使用csv模块(来自Python 3.x标准库)是此问题的基本特征1

该示例的下一个文件是一个脚本,旨在代替“客户端代码”换句话说,此脚本超出此示例的“实际目的”在很大程度上是无关紧要的。它在示例中的作用是提供一种简单,可靠的方法来引发该buggy功能的问题(例如,某些代码可以重新用于测试套件中的测试用例。)

#!/usr/bin/env python3

# myscript.py

import sys
import mymod

def print_row(row):
    print(*row, sep='\t')

def main(csvfile, mode=None):
    if mode == 'first':
        print_row(next(mymod.buggy(csvfile)))
    else:
        for row in mymod.buggy(csvfile):
            print_row(row)

if __name__ == '__main__':
    main(*sys.argv[1:])

该脚本将CSV文件的路径作为必需参数和可选的第二个参数。如果省略第二个参数,或者它不是字符串"first",则脚本将打印为stdoutCSV文件中的信息,但格式TSV如果第二个参数是字符串"first",则仅打印第一行中的信息。

StopIteration我尝试捕获异常是在myscript.py使用空文件并将字符串"first"作为参数2调用脚本时发生的

这是此代码的示例:

% cat ok_input.csv
1,2,3
4,5,6
7,8,9
% ./myscript.py ok_input.csv
1   2   3
4   5   6
7   8   9
% ./myscript.py ok_input.csv first
1   2   3
% cat empty_input.csv
# no output (of course)
% ./myscript.py empty_input.csv
# no output (as desired)
% ./myscript.py empty_input.csv first
Traceback (most recent call last):
  File "./myscript.py", line 19, in <module>
    main(*sys.argv[1:])
  File "./myscript.py", line 13, in main
    print_row(next(mymod.buggy(csvfile)))
StopIteration

问:如何StopIterationbuggy函数的词法范围内防止或捕获此异常


重要提示:请记住,在上面给出的示例中,myscript.py脚本是“客户端代码”的替代,因此不在我们的控制范围内。这意味着,任何需要更改myscript.py脚本的方法都无法解决实际的实际问题,因此,对于该问题而言,这不是可接受的答案。

上面显示的简单示例与我们的实际情况之间的一个重要区别是,在我们的情况下,有问题的输入流不是来自空文件。可以说,问题发生在buggy(或者,与其实际情况相对应)“太早”到达此流的末尾的情况。

我认为如果我可以测试任一行是否stream在终点之前就足够for row in reader:,但是我也没有找到一种执行此操作的方法。测试by返回的值stream.read(1)是0还是1会告诉我stream是否在其末尾,但是在后一种情况下stream,的内部指针将指向指向其csvfile内容的一个字节(既不stream.seek(-1, 1)也不stream.tell()在这一点上的工作。)


最后,对于任何想发布此问题答案的人:如果您要利用我上面提供的示例代码在发布提案之前对其进行测试,那将是最有效的。


编辑:mymod.py我尝试过的一种变化是:

import csv  # essential!

def buggy(csvfile):
    with open(csvfile) as stream:

        reader = csv.reader(stream)

        try:
            firstrow = next(reader)
        except StopIteration:
            firstrow = None

        if firstrow != None:
            yield firstrow

        for row in reader:
            yield row

此变体失败,并显示与原始版本几乎相同的错误消息。

当我第一次阅读@mcernak的建议时,我认为它与上述变体非常相似,因此希望它也会失败。然后我惊喜地发现事实并非如此!因此,截至目前,有一个确定的候选人可以获得赏金。就是说,我很想了解为什么上面的变体在@mcernak成功的同时却无法捕获异常。


1我要处理的实际情况是遗留代码;csv短期来看,模块切换到其他方法不是我们的选择。

2请完全忽略以下问题:演示脚本以空文件和字符串"first"作为参数调用时,其“正确响应应为” StopIteration在这篇文章的演示中,引发异常的特定输入组合并不代表导致我们的代码发出有问题的StopIteration异常的真实情况。因此,演示脚本对空文件加"first"字符串组合的“正确响应”(无论可能是什么)与我要处理的现实问题无关。

麦克纳克

您可以通过以下方式将StopIteration异常捕获在buggy函数的词法范围内

import csv  # essential!

def buggy(csvfile):
    with open(csvfile) as stream:

        reader = csv.reader(stream)

        try:
            yield next(reader)
        except StopIteration:
            yield 'dummy value'

        for row in reader:
            yield row

您基本上是从reader迭代器手动请求第一个值,然后

  • 如果成功,则从csv文件中读取第一行,并提供给buggy函数的调用者
  • 如果失败,例如空的csv文件,dummy value则会产生一些字符串,例如,以防止buggy函数的调用程序崩溃

之后,如果csv文件不为空,则将在for循环中读取(并产生)剩余的行。


编辑:为了说明为什么mymod.py问题中提到的其他变体不起作用,我向其中添加了一些打印语句:

import csv  # essential!

def buggy(csvfile):
    with open(csvfile) as stream:

        reader = csv.reader(stream)

        try:
            print('reading first row')
            firstrow = next(reader)
        except StopIteration:
            print('no first row exists')
            firstrow = None

        if firstrow != None:
            print('yielding first row: ' + firstrow)
            yield firstrow

        for row in reader:
            print('yielding next row: ' + row)
            yield row

        print('exiting function open')

运行它会得到以下输出:

% ./myscript.py empty_input.csv first
reading first row
no first row exists
exiting function open
Traceback (most recent call last):
  File "myscript.py", line 15, in <module>
    main(*sys.argv[1:])
  File "myscript.py", line 9, in main
    print_row(next(mymod.buggy(csvfile)))

这表明,如果输入文件为空,则第一个try..except块可以正确处理StopIteration异常,并且该buggy功能可以正常继续。在这种情况下
buggygets的调用者的例外是由于该buggy函数在完成之前不产生任何值这一事实

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

PostgreSQL:如何在函数中捕获异常?

来自分类Dev

如何捕获已被 Python 中的嵌套函数调用捕获的异常

来自分类Dev

如何在构造函数中捕获异步方法的异常?

来自分类Dev

如何捕获在C函数调用的Haskell回调函数中引发的Haskell异常?

来自分类Dev

从构造函数中调用构造函数并捕获异常

来自分类Dev

如何防止多次捕获异常

来自分类Dev

如何在PowerShell中捕获异常?

来自分类Dev

如何在向量中捕获.erase异常?

来自分类Dev

如何在python中捕获异常消息?

来自分类Dev

如何在PowerShell中捕获异常?

来自分类Dev

如何在nSpec中捕获异常

来自分类Dev

如何在标签中设置捕获异常?

来自分类Dev

如何在iOS中捕获此异常?

来自分类Dev

如何在Java中捕获异常?

来自分类Dev

如何在Python Azure函数中捕获Azure表引发的异常

来自分类Dev

如何防止在Jquery中多次调用函数

来自分类Dev

如何从构造函数中捕获异常而不处理整个函数?

来自分类Dev

如何从C ++中的构造函数中捕获异常

来自分类Dev

如何在C#中的函数内部捕获代码块调用的执行时间

来自分类Dev

如何捕获构造函数异常?

来自分类Dev

在构造函数中捕获异常

来自分类Dev

如何防止来电者捕获我的异常

来自分类Dev

如何在lambda中捕获函数结果?

来自分类Dev

如何从扭曲的inlineCallbacks装饰函数中捕获异常?

来自分类Dev

如何从成员析构函数中捕获异常

来自分类Dev

如何在python函数中添加异常?

来自分类Dev

如何在函数中引发异常

来自分类Dev

如何在异步函数中抛出异常?

来自分类Dev

是否有更好的方法吞噬python中yield生成的StopIteration异常?

Related 相关文章

热门标签

归档