関数で例外を発生させるか、なしを返すか?


87

Pythonのユーザー定義関数のより良い方法は何ですか:raise例外またはreturn None?たとえば、フォルダ内の最新のファイルを検索する関数があります。

def latestpdf(folder):
    # list the files and sort them
    try:
        latest = files[-1]
    except IndexError:
        # Folder is empty.
        return None  # One possibility
        raise FileNotFoundError()  # Alternative
    else:
        return somefunc(latest)  # In my case, somefunc parses the filename

別のオプションは、例外を残して呼び出し元のコードで処理することですが、FileNotFoundErrorよりもを処理する方が明確だと思いIndexErrorます。それとも、別の名前で例外を再発生させるのは悪い形式ですか?



3
私は例外を発生させることに傾倒しているので、呼び出し元の関数で例外を処理することを余儀なくされています。呼び出し元の関数で出力がNoneかどうかを確認するのを忘れると、潜在的なバグが発生する可能性があります。Noneを返した場合、呼び出し元の関数の次の行でAttributeErrorが発生することを願っています。ただし、戻り値が辞書に追加された後、別のソースファイルで100回の関数呼び出しが発生すると、AttributeErrorが発生し、その値がNoneであった理由を探すのが楽しくなります。
IceArdor 2014

一般に、特別な意味を持つ値や、1つの関数に対して複数の署名を持つ値(文字列またはNoneを返す可能性がある)も避けます。
IceArdor 2014

回答:


92

それは本当に意味論の問題です。どういうfoo = latestpdf(d) 意味ですか?

最新のファイルがないことは完全に合理的ですか?次に、確かに、Noneを返します。

常に最新のファイルを見つけることを期待していますか?例外を発生させます。そして、はい、より適切な例外を再発生させることは問題ありません。

これが任意のディレクトリに適用されるはずの一般的な関数である場合は、前者を実行してNoneを返します。たとえば、ディレクトリが、アプリケーションの既知のファイルセットを含む特定のデータディレクトリである場合は、例外を発生させます。


3
考慮すべきもう1つのポイント:例外を発生させた場合、メッセージを添付できますが、を返すときにそれを行うことはできませんNone
kawing-chiu 2017

10

それがあなたのために質問に答えるかもしれないので、私はあなたの質問に答える前にいくつかの提案をします。

  • 関数には常にわかりやすい名前を付けてください。 latestpdf誰にとってもほとんど意味がありませんlatestpdf()が、関数を調べると最新のpdfが得られます。名前を付けることをお勧めしますgetLatestPdfFromFolder(folder)

これを行うとすぐに、何を返す必要があるかが明確になりました。PDFがない場合は、例外を発生させます。しかし、もっと待ってください。

  • 機能を明確に定義してください。somefucが何をするのかが明確ではなく、最新のpdfの取得にどのように関連するかが(明らかに)明らかではないため、移動することをお勧めします。これにより、コードがはるかに読みやすくなります。

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

お役に立てれば!


1
またはget_latest_pdf_from_folder。実際、Pep8:「関数名は小文字で、読みやすさを向上させるために必要に応じて単語をアンダースコアで区切る必要があります。」
PatrickT

7

Pythonは動的に型指定されるため、私は通常、例外を内部で処理することを好みます(つまり、呼び出された関数内でtry / exceptionを実行し、場合によってはNoneを返します)。一般に、私はそれを何らかの方法で判断呼び出しと見なしますが、動的に型指定された言語では、呼び出し元に例外を渡さないことを支持してスケールを傾ける小さな要因があります。

  1. 関数を呼び出す人には、スローされる可能性のある例外は通知されません。あなたが探している例外の種類を知ることは少し芸術的な形になります(そしてブロックを除いて一般的なものは避けるべきです)。
  2. if val is Noneより少し簡単ですexcept ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace。真剣に、私from django.core.exceptions import ObjectDoesNotExistは本当に一般的なユースケースを処理するためだけに、すべてのdjangoファイルの先頭に入力することを忘れないでください。静的に型付けされた世界では、エディターに任せてください。

ただし、正直なところ、これは常に判断の呼び出しであり、呼び出された関数が解決できないエラーを受け取るという、説明している状況は、意味のある例外を再発生させる優れた理由です。あなたは正確に正しい考えを持っていますが、例外がない限り、スタックトレースでより意味のある情報を提供するつもりです

AttributeError: 'NoneType' object has no attribute 'foo'

これは、10回のうち9回、未処理のNoneを返した場合に発信者に表示されるものです。気にしないでください。

(この種のすべてcauseは、Javaのように、Python例外がデフォルトで属性を持っていることを望みます。これにより、例外を新しい例外に渡して、必要なものをすべて再スローし、問題の元のソースを失うことはありません。)


考えられる例外が定義されていないため、キャッチするのが難しいという議論は、Pythonにとって非常に有効な議論です。
snorberhuis

4

Python 3.5のタイピングで

Noneを返すときの関数の例は次のようになります。

def latestpdf(folder: str) -> Union[str, None]

例外を発生させると、次のようになります。

def latestpdf(folder: str) -> str 

オプション2はより読みやすくPythonICのようです

(+前述のように例外にコメントを追加するオプション。)


5
Union[str, None]あるべきOptional[str]
Georgy 2018

2
速記ですが、あなたは正しいです、それはより読みやすいです。編集していないので、両方のオプションがここにあります。
asaf 2018

1
2は潜在的に読みやすくなりますが、(残念ながら?)タイプヒントは例外がスローされる可能性があることを示していません。最近、Noneの戻りを処理する必要があるため、1がより多くのエラーをキャッチするのに役立つことがわかりました。
jonespm

2

一般に、回復できない壊滅的な問題が発生した場合(つまり、関数が接続できないインターネットリソースを処理する場合)は例外をスローし、関数が実際に何かを返す必要がある場合はNoneを返す必要があります。ただし、返すのに適切なものはありません(たとえば、関数が文字列内の部分文字列と一致させようとする場合は「なし」)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.