2012-01-19 18 views
5

自分のPythonプログラムでコンストラクタパラメータを正しい型に変換しようとしていることがよくあります。今まで私はこれに似たコードを使用していましたので、例外引数を繰り返す必要はありません:Pythonのプラクティス:コンストラクタのパラメータをチェックする良い方法はありますか?

class ClassWithThreads(object): 
    def __init__(self, num_threads): 
     try: 
      self.num_threads= int(num_threads) 
      if self.num_threads <= 0: 
       raise ValueError() 
     except ValueError: 
      raise ValueError("invalid thread count") 

これは良い方法ですか?変換に際して例外をキャッチして通話者に伝播させるだけで、意味のない一貫したエラーメッセージが出る可能性があるというデメリットがありますか?

+2

ユーザ向けのインターフェイスでない場合は、そのままにして、例外を自然に伝播させます。あなたは自分でもっと仕事をしていますし、例外をラップしても大きな価値はありません。 – monkut

答えて

11

このような質問があるときは、コードをモデル化できる標準ライブラリを探しています。それは

processes = int(processes) 

それはちょうどあなたがそれを整数ではなく、浮動小数点数や文字列、または何を送ったと仮定し言っていないことを

class Pool(object): 

    def __init__(self, processes=None, initializer=None, initargs=(), 
       maxtasksperchild=None): 
     ... 
     if processes is None: 
      try: 
       processes = cpu_count() 
      except NotImplementedError: 
       processes = 1 
     if processes < 1: 
      raise ValueError("Number of processes must be at least 1") 

     if initializer is not None and not hasattr(initializer, '__call__'): 
      raise TypeError('initializer must be a callable') 

お知らせ:multiprocessing/pool.pyはあなたにやや近いクラスがあります。 かなり分かりやすいはずですが、そうではないと感じたら、それを文書化するだけで十分です。

processes < 1の場合はValueErrorを発行し、指定されている場合はinitializerが呼び出し可能であることを確認します。

私たちがモデルとしてmultiprocessing.Poolを取るのであれば、あなたのクラスには、次のようになります。

class ClassWithThreads(object): 
    def __init__(self, num_threads): 
     self.num_threads = num_threads 
     if self.num_threads < 1: 
      raise ValueError('Number of threads must be at least 1') 

は、このアプローチは、おそらくいくつか 条件のために非常に予測不可能に失敗しませんか?

私は先制型チェックは、一般的にPythonの (するダイナミック、ダックタイピング)設計理念の穀物に反すると思います。

ダックタイピングPythonプログラマに素晴らしい表現力の機会、 と迅速なコード開発を与えるが(一部は言うかもしれない)、それは型エラーをキャッチするために何 試みをしないため危険です。

論理エラーは、エラー よりもはるかに深刻で頻繁であると主張する人もいます。より重大なエラーを検出するには、単体テストが必要です。したがって、 でもプリエンプティブタイプのチェックをしても、それほどの保護はありません。

この議論は、事実ではなく意見の領域にあるため、解決できる議論ではありません。フェンス のどちらの側に座っているかは、あなたの経験、タイプ の可能性に関するあなたの判断に依存する可能性があります。すでに知っている言語で偏っているかもしれません。問題のドメイン に依存することがあります。

自分で決めなければなりません。


PS。静的型付き言語では、コンパイル時に型チェックを行うことができるため、プログラムの速度を損なうことはありません。 Pythonでは、型チェックは実行時に行わなければなりません。これにより、プログラムが少し遅くなります。ループ内でチェックが行われている場合は、多分多分です。プログラムが増えると、型チェックの数も増えます。残念ながら、これらの小切手の多くは重複している可能性があります。したがって、実際に型チェックが必要と思われる場合は、静的型の言語を使用する必要があります。


PPS。 (Python 2)と(Python 3)の型チェックのためのデコレータがあります。これにより、タイプチェックコードが残りの関数と分離され、将​​来的にタイプチェックをオフにすることができます。

+0

このアプローチは、場合によっては予期せず非常に失敗しないでしょうか?たとえば、文字列を渡すことはエラーとして捕捉されません( '<1'として比較されることはないため)。プリエンプティブチェックを行う私の意図は、オブジェクトの状態の一貫性を保証し、あとでランダムに関連する例外を投げるのではなく、できるだけ早くエラーを検出することです。あとで物事をして失敗させるのは、もっと「pythonic」ですか? – danielkza

4

this activestate recipeまたはthis other one for python 3のようなタイプチェックデコレータを使用できます。これらは、次のようなコードを書くことを可能にします:

@require("x", int, float) 
@require("y", float) 
def foo(x, y): 
    return x+y 

引数が必須の型でない場合、例外が発生します。引数が有効な値を持っていることを確認するためにデコレータを簡単に拡張することもできます。

+2

これらのデコレータのレシピは素晴らしく(もう必要ない場合は簡単にオフにすることができます)。 –

+0

これはこれまでの中で最も洗練された解決策であるようですが、私は間違いなくそれをチェックします。無関係なメモで、activestate.comはFirefox(10.0)を他の誰かのためにハングアップしますか? – danielkza

2

これは主観的であるが、ここで反論です:

>>> obj = ClassWithThreads("potato") 
ValueError: invalid thread count 

待って、何?それはTypeErrorでなければなりません。

if not isinstance(num_threads, int): 
    raise TypeError("num_threads must be an integer") 
if num_threads <= 0: 
    raise ValueError("num_threads must be positive") 

わかりましたので、これは「ダックタイピング」の原則に違反:私はこれを行うだろう。しかし、私はintのようなプリミティブなオブジェクトにはダックタイピングを使用しません。

関連する問題