亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何防止或捕獲yield 調用函數中的StopIteration 異常?

如何防止或捕獲yield 調用函數中的StopIteration 異常?

MMMHUHU 2023-10-26 10:45:08
我們的庫之一中的生成器返回函數(即其中包含yield語句的函數)由于未處理的StopIteration異常而導致某些測試失敗。為了方便起見,在這篇文章中我將此函數稱為buggy。我一直無法找到一種方法來buggy防止異常(不影響函數的正常運行)。同樣,我還沒有找到一種方法來捕獲異常(使用try/ except)buggy。(客戶端代碼usingbuggy可以捕獲此異常,但這發生得太晚了,因為具有正確處理導致此異常的條件所需信息的代碼是函數buggy。)我正在使用的實際代碼和測試用例太復雜,無法在此處發布,因此我創建了一個非常簡單但又極其人為的玩具示例來說明問題。一、模塊功能buggy:# mymod.pyimport 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.pyimport sysimport mymoddef 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",則腳本將以TSVstdout格式打印到CSV 文件中的信息。如果第二個參數是字符串,則僅打印第一行中的信息。"first"當使用空文件和字符串作為參數2StopIteration調用腳本時,會出現我試圖捕獲的異常。myscript.py"first"
查看完整描述

2 回答

?
MMTTMM

TA貢獻1869條經驗 獲得超4個贊

mcernak很好地解決并描述了您遇到的問題


然而,這個問題背后存在一個設計問題:調用者有時并不期望生成器,而是非空迭代器


從另一個角度來看,如果文件丟失了怎么辦?對于函數句柄并返回一些哨兵或將其提升給調用者是否更有IOError意義open?


不要試圖強制你的生成器與虐待它的調用者一起工作,而是考慮


提供兩個函數(一個可以調用另一個)

為生成器的最大行數提供一個參數(可能是最好的)

# mymod.py


import csv

import itertools

def notbuggy(csvfile, max_rows=None):

    with open(csvfile) as stream:

        yield from itertools.islice(csv.reader(stream), max_rows)

#!/usr/bin/env python3

# myscript.py


import sys

import mymod


def print_row(row):

    print(*row, sep='\t')


def main(csvfile, mode=None):

    max_rows = 1 if mode == "first" else None

    for row in mymod.notbuggy(csvfile, max_rows):

        print_row(row)


if __name__ == '__main__':

    main(*sys.argv[1:])


使用時next(),調用邏輯必須同意以下之一


永遠不要在空的可迭代對象上調用它(先檢查文件?)

處理來自生成器的異常(StopIteration一些自定義Exception)

處理一些空的哨兵(也許""是一些字符串,None或object..)

然而,調用者沒有執行這些操作,因此保證沒有很好地設置!


如果調用者想要多個行或將空哨兵解釋為值怎么辦?除非這些在文檔中以某種方式傳達,否則調用者總是可以誤用函數并且不知道為什么它會出現意外的行為。


>>> next(iter(()))

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

>>> g = iter((1,))

>>> next(g)

1

>>> next(g)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

>>> print_row("STOP SENTINEL")

S   T   O   P       S   E   N   T   I   N   E   L


查看完整回答
反對 回復 2023-10-26
?
喵喵時光機

TA貢獻1846條經驗 獲得超7個贊

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函數會正常繼續。

在這種情況下,調用者得到的異常buggy是由于該buggy函數在完成之前不會產生任何值。


查看完整回答
反對 回復 2023-10-26
  • 2 回答
  • 0 關注
  • 213 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號