コンストラクター引数のタイプをチェックする必要があります(他の場所でも)。
-
03-07-2019 - |
質問
Pythonは型のチェックを推奨していません。しかし、多くの場合、これは役に立つかもしれません:
-
コンストラクタの引数を確認しています。例えばブール値、文字列、辞書などをチェックします。オブジェクトのメンバーを引数に設定しないと、後で問題が発生します。
-
関数の引数の確認。
-
プロパティ内。誰かが間違った値または異なるタイプを設定した場合、私は迅速に対応する必要があります。
解決
単純な答えはいいえです。ポリモーフィズム、例外などを使用します。
-
コンストラクター引数のタイプが間違っている場合、特定のタイプのパラメーターに依存するコードを実行すると、例外がスローされます。ドメイン固有の奇妙なものであれば、独自の例外を発生させます。 try-exceptで失敗し、エラーを処理する可能性が高いコードのブロックを囲みます。したがって、例外処理を使用することをお勧めします。 (関数の引数についても同様です)
-
プロパティでは、同じ引数が適用されます。受け取った値を検証している場合は、アサーションを使用してその範囲などを確認します。値のタイプが間違っていると、とにかく失敗します。次に、AssertionErrorを処理します。
Pythonでは、プログラマを知的存在として扱います!!コードを適切に文書化し(明確にする)、必要に応じて例外を発生させ、多態的なコードなどを記述します。
警告
例外処理をクライアントに任せたからといって、知らないうちに大量のガベージエラーが発生するわけではありません。可能な場合は、コード自体の構造不良やその他の理由により発生する可能性のある例外を処理します。コードは堅牢でなければなりません。エラーを処理することが不可能な場合は、ユーザー/クライアントコードプログラマーに丁寧に知らせてください!
注
一般的に、コンストラクターへの不適切な引数は、私があまり心配するものではありません。
他のヒント
ほとんどの場合、答えは「いいえ」です。 Python、Ruby、および他のいくつかの言語の一般的なアイデアは" Duck Typing "と呼ばれています。あなたは何かが何であるかを気にするべきではなく、それがどのように機能するかだけを気にするべきではありません。言い換えれば、「必要なのが鳴るだけなら、それが実際にアヒルであることを確認する必要はありません」
実際には、これらすべての型チェックを行う際の問題は、入力を代替実装に置き換えることができないことです。 dictを確認することはできますが、dictではなく、dict APIを実装するものを渡すことができます。
型チェックは、コード内の多くの可能性のあるエラーの1つのみをチェックします。たとえば、範囲チェックは含まれていません(少なくともPythonではそうではありません)。型チェックが必要であるという主張に対する現代の対応は、型が正しいだけでなく機能も正しいことを確認する単体テストを開発する方がより効果的だということです。
もう1つの観点は、APIユーザーを同意した大人のように扱い、APIを正しく使用することを信頼することです。もちろん、入力チェックが役立つ場合もありますが、それはあなたが思っているほど一般的ではありません。 1つの例は、パブリックWebなどからの信頼できないソースからの入力です。
好きなものをすべてチェックしてください。明示する必要があります。次の例は、 aモジュール標準ライブラリで- extrasaction
arg:
class DictWriter:
def __init__(self, f, fieldnames, restval="", extrasaction="raise",
dialect="excel", *args, **kwds):
self.fieldnames = fieldnames # list of keys for the dict
self.restval = restval # for writing short dicts
if extrasaction.lower() not in ("raise", "ignore"):
raise ValueError, \
("extrasaction (%s) must be 'raise' or 'ignore'" %
extrasaction)
self.extrasaction = extrasaction
self.writer = writer(f, dialect, *args, **kwds)
多くの場合、それは良いことです。明示的な型のチェックはおそらくPythonでそれほど有用ではありません(他の人が言っているように)が、正当な値のチェックは良い考えです。良いアイデアだという理由は、ソフトウェアがバグの原因に近いところで失敗するということです(Fail Fast Principleに従います)。また、チェックは他のプログラマーとあなた自身への文書として機能します。さらに良いのは、「実行可能なドキュメント」であり、うそをつくことができないドキュメントであるため良いことです。
引数をチェックするための手っ取り早い方法ですが、妥当な方法はassertを使用することです:
def my_sqrt(x):
assert x >= 0, "must be greater or equal to zero"
# ...
あなたの議論を主張することは、契約による一種の貧乏人のデザインです。 (Design by Contractを検索することもできます。それは興味深いです。)
AFAIU、いくつかのオブジェクトが実際の使用よりも早い時点で動作する(「インターフェイスに従う」)ことを確認する必要があります。この例では、オブジェクトが実際に使用されるときではなく、インスタンス作成時に適切であることを知りたいと考えています。
ここでPythonについて話していることに留意して、 assert
( python -O
または環境変数PYTHONOPTIMIZEが1に設定されている場合はプログラムを実行しますか?)または特定の型をチェックします(使用できる型が不必要に制限されるため)
def __init__(self, a_number, a_boolean, a_duck, a_sequence):
self.a_number= a_number + 0
self.a_boolean= not not a_boolean
try:
a_duck.quack
except AttributeError:
raise TypeError, "can't use it if it doesn't quack"
else:
self.a_duck= a_duck
try:
iter(a_sequence)
except TypeError:
raise TypeError, "expected an iterable sequence"
else:
self.a_sequence= a_sequence
try…を使用しましたを除く…コードが変更または拡張された場合でも、テストが成功した場合にのみ をインスタンスメンバーに設定するため、この提案ではelse
を使用します。もちろんそうする必要はありません。
関数の引数とプロパティの設定については、事前にこれらのテストを行うのではなく、提供されたオブジェクトを使用し、長時間のプロセスの後に疑わしいオブジェクトが使用される場合を除き、スローされた例外を処理します。
"オブジェクトのメンバーを引数に設定せずに設定すると、後で問題が発生します。"
「問題」の正確なリストを非常に明確にしてください。後で発生します。
-
まったく機能しませんか? try / exceptブロックの目的。
-
「奇妙な」動作をしますか?これは非常にまれであり、「ニアミス」に限定されます。タイプと演算子。標準的な例は除算です。整数を期待しているが、浮動小数点を取得している場合、除算は希望どおりに動作しない可能性があります。しかし、これは//、vs。/除算演算子で修正されています。
-
それは単に間違っているのでしょうか、それでも完了したように見えますか?これは非常にまれであり、標準的な名前を使用するが、非標準的なことを行う、かなり慎重に作成されたタイプが必要です。例
class MyMaliciousList( list ): def append( self, value ): super( MyMaliciousList, self ).remove( value )
それ以外は、「後で問題が発生する」ということはありません。 「問題」の具体例で質問を更新してください。
dalkeが言うように、答えはほとんど常に「いいえ」です。 Pythonでは、通常、パラメータが特定の型であることを気にせず、特定の型のように振る舞う 。これは" ダックタイピング"として知られています。パラメーターが特定の型のように動作するかどうかをテストするには、2つの方法があります。(1)期待どおりに動作するかのように使用し、そうでない場合またはそうでない場合に例外をスローできます(2 )そのタイプがどのように振る舞うかを記述するインターフェースを定義し、そのインターフェースへの適合性をテストすることができます。
zope.interface は私の好みのインターフェースですPython用のシステムですが、他にもいくつかあります。それらのいずれかを使用して、インターフェイスを定義し、特定の型がそのインターフェイスに準拠することを宣言するか、タイプをそのインターフェイスに準拠するものに変換するアダプタを定義します。その後、パラメーターが(zope.interface用語で)そのインターフェイスを提供することをアサート(または、必要に応じてテスト)できます。