generalでは、次のようなメソッドがあるとしましょう。
def intersect_two_lists(self, list1, list2):
if not list1:
self.trap_error("union_two_lists: list1 must not be empty.")
return False
if not list2:
self.trap_error("union_two_lists: list2 must not be empty.")
return False
#http://bytes.com/topic/python/answers/19083-standard
return filter(lambda x:x in list1,list2)
エラーが見つかったこの特定のメソッドでは、この特定のメソッド呼び出しに対する本当の答えであった可能性があるため、この場合は空のリストを返したくありません。パラメータが正しくなかったことを示す何かを返します。したがって、この場合はエラー時にFalseを返し、それ以外の場合は空のリストを返しました。
私の質問は、リストだけでなく、このような分野でのベストプラクティスは何ですか?私が望むものは何でも返して、ユーザーが読むことができるように文書化してください? :-)あなたのほとんどの人々は何をしますか:
まず、結果とエラーメッセージを返さない場合。これはエラーを処理するための非常に悪い方法であり、無限の頭痛の種になります。 エラーを示す必要がある場合は常に例外を発生させます。
私は通常、必要でない限りエラーの発生を避ける傾向があります。あなたの例では、エラーを投げることは本当に必要ありません。空のリストを空でないリストと交差させることはエラーではありません。結果は単なる空のリストであり、それは正しいです。しかし、他のケースを処理したいとしましょう。たとえば、メソッドがリスト以外の型を取得した場合。この場合、例外を発生させることをお勧めします。例外は恐れることではありません。
あなたへの私のアドバイスは、Python同様の関数のライブラリを見て、Pythonがこれらの特殊なケースをどのように処理するかを確認することです。ここでは、空のセットと空のリストを交差させようとしています。
>>> b = []
>>> a = set()
>>> a.intersection(b)
set([])
>>> b = [1, 2]
>>> a = set([1, 3])
>>> a.intersection(b)
set([1])
エラーは必要な場合にのみスローされます:
>>> b = 1
>>> a.intersection(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
もちろん、成功または失敗したときにTrueまたはFalseを返すことが適切な場合があります。しかし、一貫性を保つことは非常に重要です。関数は常に同じ型または構造を返す必要があります。リストまたはブール値を返す可能性のある関数があると、非常に混乱します。または、同じ型を返しますが、エラーの場合、この値の意味は異なる場合があります。
編集:
OPは言う:
パラメーターが正しくなかったことを示すために何かを返したいです。
例外よりも優れたエラーがあるということは何もありません。パラメータが正しくないことを示したい場合は、例外を使用して有用なエラーメッセージを入力してください。この場合、結果を返すのは混乱を招くだけです。何も起きていないがエラーではないことを示したい場合があります。たとえば、テーブルからエントリを削除するメソッドがあり、削除が要求されたエントリが存在しない場合。この場合、成功または失敗したときにTrueまたはFalseを返すだけでよい場合があります。それはアプリケーションと意図された動作に依存します
例外を発生させる の方が特別な値を返すよりも良いでしょう。これは、エラーコードをより堅牢で構造化されたエラー処理メカニズムに置き換えるために設計された例外です。
class IntersectException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def intersect_two_lists(self, list1, list2):
if not list1: raise IntersectException("list1 must not be empty.")
if not list2: raise IntersectException("list2 must not be empty.")
#http://bytes.com/topic/python/answers/19083-standard
return filter(lambda x:x in list1,list2)
この特定のケースでは、おそらくテストをやめるだけでしょう。空のリストが交差しても問題はありません。また、lambda
は、リスト内包表記よりも最近では推奨されていません。 lambda
を使用せずにこれを記述する方法については、 2つのリストの共通部分を検索しますか? を参照してください。
タプルを返すのが好きです:
(True、some_result)
(False、some_useful_response)
some_useful_responseオブジェクトは、戻り条件の処理に使用したり、デバッグ情報の表示に使用したりできます。
[〜#〜] note [〜#〜]:この手法は、あらゆる種類の戻り値に適用されます。 例外の場合と間違えないでください。
受信側では、開梱するだけです。
コード、応答= some_function(...)
この手法は、「通常の」制御フローに適用されます。予期しない入力/処理が発生した場合、例外機能を使用する必要があります。
注目に値する:この手法は、関数の戻り値を正規化するのに役立ちます。プログラマーと関数のユーザーの両方期待することを知っている。
免責事項:私はアーランのバックグラウンドから来ました:-)
例外は、ステータスが返されるよりも間違いなく優れています(さらにPython的な)。これに関する詳細: 例外とステータスの戻り値
一般的なケースは、例外的な状況で例外をスローすることです。正確な引用(または誰が言ったのか)を覚えておいてほしいのですが、合理的な数の値と型を受け入れ、非常に厳密に定義された動作を維持する関数を探す必要があります。これは、 ナディアが話していた の変形です。関数の次の使用法を考慮してください。
intersect_two_lists(None, None)
intersect_two_lists([], ())
intersect_two_lists('12', '23')
intersect_two_lists([1, 2], {1: 'one', 2: 'two'})
intersect_two_lists(False, [1])
intersect_two_lists(None, [1])
False
を渡すことは型エラーであるため、(5)は例外をスローすると予想されます。ただし、それらの残りの部分は何らかの意味をなしますが、実際には関数が記述するコントラクトに依存します。 intersect_two_lists
は、2つの iterables の共通部分を返すものとして定義されていたため、None
を空のセット。実装は次のようになります。
def intersect_two_lists(seq1, seq2):
if seq1 is None: seq1 = []
if seq2 is None: seq2 = []
if not isinstance(seq1, collections.Iterable):
raise TypeError("seq1 is not Iterable")
if not isinstance(seq2, collections.Iterable):
raise TypeError("seq1 is not Iterable")
return filter(...)
私は通常、契約が何であれ実施するヘルパー関数を作成し、それらを呼び出してすべての前提条件をチェックします。何かのようなもの:
def require_iterable(name, arg):
"""Returns an iterable representation of arg or raises an exception."""
if arg is not None:
if not isinstance(arg, collections.Iterable):
raise TypeError(name + " is not Iterable")
return arg
return []
def intersect_two_lists(seq1, seq2):
list1 = require_iterable("seq1", seq1)
list2 = require_iterable("seq2", seq2)
return filter(...)
この概念を拡張し、オプションの引数として "policy"を渡すこともできます。 Policy Based Design を受け入れたくない限り、これを行うことはお勧めしません。以前にこのオプションを検討したことがない場合に備えて、言及したかったのです。
intersect_two_lists
は、2つの空でないlist
パラメーターのみを受け入れ、明示的であり、契約に違反した場合に例外をスローすることです。
def require_non_empty_list(name, var):
if not isinstance(var, list):
raise TypeError(name + " is not a list")
if var == []:
raise ValueError(name + " is empty")
def intersect_two_lists(list1, list2):
require_non_empty_list('list1', list1)
require_non_empty_list('list2', list2)
return filter(...)
物語の教訓は、あなたが何をするにしても、一貫してそれを行い、明示的であると思います。個人的に、私は通常、契約に違反したとき、または本当に使用できない値が与えられたときはいつでも例外を上げることを好みます。与えられた値が合理的であれば、見返りに合理的なことをしようとします。また、例外について C++ FAQ Liteエントリ を読むこともできます。この特定のエントリは、思考のためのより多くの食物を提供します例外について。
コレクション(リスト、セット、辞書など)の場合、空のコレクションを返すことは明らかな選択です。これは、呼び出しサイトロジックが防御ロジックを排除できるようにするためです。より明確に、空のコレクションは、コレクションを期待する関数からの完全に良い答えであり、結果が他のタイプのものであることを確認する必要がなく、ビジネスロジックをクリーンな方法で継続できます。
コレクション以外の結果の場合、条件付きリターンを処理する方法がいくつかあります。
None
を返すことですが、これによりユーザーはコールサイトのあらゆる場所に防御チェックを追加し、実際のビジネスロジックを難読化します彼らは実際に実行しようとしています。pip install optional.py
を使用してインストールできます。コメント、機能のリクエスト、貢献を歓迎します。