リスト内包表記の例外を処理する方法は?


120

私はいくつかのリストの理解をPythonで持っていますが、各反復で例外がスローされる可能性があります。

たとえば、私が持っている場合:

eggs = (1,3,0,3,2)

[1/egg for egg in eggs]

ZeroDivisionError3番目の要素で例外が発生します。

この例外を処理してリスト内包表記の実行を継続するにはどうすればよいですか?

私が考えることができる唯一の方法はヘルパー関数を使用することです:

def spam(egg):
    try:
        return 1/egg
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

しかし、これは私には少し厄介に見えます。

Pythonでこれを行うより良い方法はありますか?

注: これは私が考案した単純な例(上記の「」を参照)です。私の実際の例にはいくつかのコンテキストが必要なためです。エラーゼロによる除算を回避するのではなく、リスト内包の例外を処理することに興味があります。


4
例外を処理する式を追加するPEP 463があります。あなたの例ではそれはでしょう[1/egg except ZeroDivisionError: None for egg in (1,3,0,3,2)]。しかし、それはまだドラフトモードです。私の直感はそれが受け入れられないだろうということです。Imho式は面倒になりすぎる可能性があります(複数の例外をチェックしたり、より複雑な組み合わせ(複数の論理演算子、複雑な内包表記など)を
持っている

1
この特定の例では、ndarrayで適切な設定をしたnumpy を使用できることに注意してくださいnp.seterr。その結果、になり1/0 = nanます。しかし、それがこのニーズが発生する他の状況に一般化されないことを私は理解しています。
gerrit

回答:


96

Pythonには、例外を無視できる(または例外の場合は代替値&cを返す)組み込みの式がないため、リスト内包表記は式であるため、文字通り「リスト内包表記の例外を処理する」ことは不可能です。他の式を含み、それ以上は含まない(つまり、ステートメントはなく、ステートメントのみが例外をキャッチ/無視/処理できる)

関数呼び出しは式であり、関数本体には必要なすべてのステートメントを含めることができます。そのため、例外が発生しやすい部分式の評価を関数に委任することは、気付いたように、1つの実行可能な回避策です(他の方法は、実行可能な場合、他の回答でも提案されているように、例外を引き起こす可能性のある値をチェックします)。

「リスト内包表記で例外を処理する方法」という質問への正しい回答はすべて、この真理のすべての一部を表しています。2)実際には、ジョブを関数に委任するか、可能であればエラーが発生しやすい値をチェックします。したがって、これが答えではないという繰り返しの主張は根拠のないものです。


14
そうですか。したがって、完全な答えは次のいずれかでなければならないということです:1.関数を使用する2.リスト内包表記を使用しない3.例外を処理するのではなく、回避するようにしてください。
Nathan Fellman、

9
「リスト内包表記で例外を処理する方法」への回答の一部として「リスト内包表記を使用しない」とは思わないが、「LCで字句的には、結果として起こり得る結果としてそれを合理的に見ることができると思う。例外を処理する」、これは確かに文字通りの答えの最初の部分です。
Alex Martelli、

ジェネレータ式またはジェネレータ内包表記のエラーをキャッチできますか?

1
@ AlexMartelli、except句は将来のpythonバージョンに取り組むのが難しいでしょうか?[x[1] for x in list except IndexError pass]。インタープリターが一時的な関数を作成して、試行できませんでしたx[1]か?
alancalvitti

上記の1、2、3の@Nathanは、関数型データフローで途方もない頭痛の種に変わります。2.代替策は、機能パラダイムに違反し、エラーが発生しやすいコードにつながる、入れ子になったforループをたくさん使用することです。3.多くの場合、エラーはアドホックで潜在的な複雑なデータセットであり、データ手段のラテン語として与えられているため、簡単には防止できません。
alancalvitti

118

この質問はかなり古いですが、一般的な関数を作成して、この種のことを簡単にすることもできます。

def catch(func, handle=lambda e : e, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return handle(e)

次に、あなたの理解の中で:

eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]

もちろん、デフォルトのハンドル関数を好きなようにすることができます(デフォルトでは「None」を返します)。

これがあなたやこの質問の将来の視聴者に役立つことを願っています!

注:Python 3では、「handle」引数キーワードのみを作成し、それを引数リストの最後に配置します。これにより、実際にcatchを介して引数などを渡すことがより自然になります。


2
非常に便利、ありがとう。理論的なコメントには同意しますが、これは私が繰り返し経験した問題を解決するための実際的なアプローチを示しています。
ポール

2
素晴らしい答え。私がお勧めする1つのmodはargskwargsパススルーと同様に処理することです。そうすることeggで、ハードコードされたの代わりに言う0か、あなたがしているように例外を返すことができます。
Mad Physicist

3
例外タイプをオプションの引数として使用することもできます(例外タイプをパラメーター化できるか?)。これにより、すべての例外を無視するのではなく、予期しない例外が上方にスローされます。
00prometheus 2017年

3
@ブライアン、「Python 3では、引数のキーワード「ハンドル」だけを作成して、引数リストの最後に置きます」のコードを提供できますか?handle後に配置し**kwargてみましたSyntaxError。として逆参照するつもりkwargs.get('handle',e)ですか?
alancalvitti

21

使用できます

[1/egg for egg in eggs if egg != 0]

これは単にゼロの要素をスキップするだけです。


28
これは、リスト内包表記で例外を処理する方法の質問には答えません。
Nathan Fellman、

8
うーん、そうです。例外を処理する必要がなくなります。はい、それは常に正しい解決策ではありませんが、それは一般的な解決策です。
ピーター

3
わかります。コメントを取り戻します(ただし、短い「ディスカッション」で回答が改善されるため、コメントは削除しません)。
Nathan Fellman、

11

いいえ、もっと良い方法はありません。多くの場合、ピーターのように回避を使用できます

他のオプションは、内包表記を使用しないことです

eggs = (1,3,0,3,2)

result=[]
for egg in eggs:
    try:
        result.append(egg/0)
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

面倒かどうかはあなた次第


1
ここで理解度をどのように使用しますか?
Nathan Fellman、

@ネイサン:あなたはしません。gnibblerさんのコメント:いいえ、これ以上の方法はありません
SilentGhost '06 / 10/22

申し訳ありません...私は彼の答えで「not」を逃しました:-)
Nathan Fellman

4

最初の質問をする人とブライアンヘッドからも示唆されているように、ヘルパー関数は優れており、面倒ではありません。すべての作業を実行する1行のマジックコードが常に可能であるとは限りませんfor。ループを回避したい場合は、ヘルパー関数が最適なソリューションです。ただし、これを次のように変更します。

# A modified version of the helper function by the Question starter 
def spam(egg):
    try:
        return 1/egg, None
    except ZeroDivisionError as err:
        # handle division by zero error        
        return None, err

出力はこれになり[(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]ます。この答えであなたはあなたが望む方法で続行するための完全な制御下にあります。


いいね。これはEither、いくつかの関数型プログラミング言語(Scalaなど)の型に非常に似ていEitherます。そこでは、型の値を含めることができますが、両方を含めることはできません。唯一の違いは、それらの言語では、左側にエラーを配置し、右側に値を配置するのが慣用的であるということです。 詳細はこちらの記事をご覧ください
Alex Palmer

3

これについて言及している回答はありませんでした。ただし、この例は、既知の失敗した場合に例外が発生するのを防ぐ1つの方法です。

eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]


Output: [1, 0, None, 0, 0]

それはこの答えと同じではありませんか?stackoverflow.com/a/1528244/1084
Nathan Fellman

微妙な違いがあります。フィルタリングは、入力リストではなく出力に適用されます。投稿された例でわかるように、例外を引き起こす可能性のあるケースについては「なし」と示しました。
Slakker、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.