Pythonでのダックタイピング、データ検証、アサーティブプログラミング


10

アヒルのタイピングについて:

アヒルの型付けは、メソッドと関数の本体の引数の型を常にテストするのではなく、ドキュメント、明確なコード、および正しい使用を確実にするためのテストに依存することによって支援されます。

引数の検証について(EAFP:許可よりも許しを求める方が簡単です)。ここからの適応例:

...それを行うにはよりpythonicと考えられています:

def my_method(self, key):
    try:
        value = self.a_dict[member]
    except TypeError:
        # do something else

これは、コードを使用する他のユーザーが実際の辞書やサブクラスを使用する必要がないことを意味します。マッピングインターフェースを実装する任意のオブジェクトを使用できます。

残念ながら、実際にはそれほど簡単ではありません。上記の例のメンバーが整数の場合はどうなりますか?整数は不変です。そのため、それらを辞書のキーとして使用することは完全に合理的です。ただし、シーケンス型オブジェクトのインデックス付けにも使用されます。メンバーが偶然整数の場合、例2ではリストと文字列、および辞書を通過できます。

断定的なプログラミングについて:

アサーションは、プログラムの内部状態がプログラマが期待したとおりであることを確認する体系的な方法であり、バグをキャッチすることを目的としています。特に、コードの記述中に行われた誤った仮定や、他のプログラマによるインターフェイスの悪用をキャッチするのに適しています。さらに、プログラマーの想定を明確にすることで、インラインドキュメントとしてある程度の役割を果たすことができます。(「明示的は暗黙的よりも優れています。」)

上記の概念は競合する場合があるため、データの検証をまったく行わないか、強力な検証を行うか、またはアサートを使用するかを選択するときは、次の要因を考慮します。

  1. 強力な検証。強力な検証とはApiError、たとえばカスタム例外を発生させることです。関数/メソッドがパブリックAPIの一部である場合は、引数を検証して、予期しないタイプに関する適切なエラーメッセージを表示することをお勧めします。タイプをチェックすることで、のみを使用することを意味するのではなくisinstance、渡されたオブジェクトが必要なインターフェース(ダックタイピング)をサポートするかどうかも確認します。APIをドキュメント化し、予想されるタイプを指定し、ユーザーが関数を予期しない方法で使用する可能性がある場合、想定を確認すると安全です。私は通常使用isinstanceし、後で他のタイプやアヒルをサポートしたい場合は、検証ロジックを変更します。

  2. 断定的なプログラミング。私のコードが新しい場合、私はアサートをたくさん使います。これに関するあなたのアドバイスは何ですか?後でコードからアサーションを削除しますか?

  3. 私の関数/メソッドがAPIの一部ではないが、自分で記述、調査、テストしていない他のコードに引数の一部を渡す場合、呼び出されたインターフェイスに従って多くのアサーションを実行します。これの背後にある私のロジック-私のコードでよりよく失敗し、スタックトレースのどこかで10レベル深く、理解できないエラーが発生するため、多くのデバッグを行い、とにかくアサートをコードに追加する必要があります。

型/値検証をいつ使用するべきか、または使用しないべきかについてのコメントとアドバイスは断言しますか?質問の構成が最適ではないため申し訳ありません。

たとえばCustomer、SQLAlchemy宣言モデルである次の関数を考えてみます。

def add_customer(self, customer):
    """Save new customer into the database.
    @param customer: Customer instance, whose id is None
    @return: merged into global session customer
    """
    # no validation here at all
    # let's hope SQLAlchemy session will break if `customer` is not a model instance
    customer = self.session.add(customer)
    self.session.commit()
    return customer

したがって、検証を処理する方法はいくつかあります。

def add_customer(self, customer):
    # this is an API method, so let's validate the input
    if not isinstance(customer, Customer):
        raise ApiError('Invalid type')
    if customer.id is not None:
        raise ApiError('id should be None')

    customer = self.session.add(customer)
    self.session.commit()
    return customer

または

def add_customer(self, customer):
    # this is an internal method, but i want to be sure
    # that it's a customer model instance
    assert isinstance(customer, Customer), 'Achtung!'
    assert customer.id is None

    customer = self.session.add(customer)
    self.session.commit()
    return customer

ダックタイピング、タイプチェック、データ検証のコンテキストでこれらのそれぞれをいつ、なぜ使用しますか?


1
パフォーマンス上の理由がない限り、単体テストと同じようにアサーションを削除しないでください
Bryan Chen

回答:


4

いくつかの指針となる原則を挙げさせてください。

原則#1。http://docs.python.org/2/reference/simple_stmts.htmlで概説されているように、アサーションのパフォーマンスオーバーヘッドは、デバッグのために存在しながら、コマンドラインオプションで削除できます。パフォーマンスに問題がある場合は、それを行ってください。アサートを残します。(しかし、アサートで重要なことは何もしないでください!)

原則#2。何かをアサートしていて、致命的なエラーが発生する場合は、アサートを使用します。他のことをする価値は全くありません。後で誰かがそれを変更したい場合は、コードを変更するか、そのメソッド呼び出しを回避できます。

原則#3。あなたが愚かなことだと思っているからといって、何かを禁止しないでください。では、メソッドで文字列の通過を許可するとどうなるでしょうか。うまくいけば、うまくいきます。

原則#4。間違いの可能性が高いことを示すものは許可しないでください。たとえば、オプションの辞書を渡されることを検討してください。その辞書に無効なオプションが含まれている場合、それは誰かがあなたのAPIを理解していないか、タイプミスがあったことを示しています。それを爆破することは、誰かが合理的なことをするのを止めることよりも、タイプミスをキャッチする可能性が高くなります。

最初の2つの原則に基づいて、2番目のバージョンは破棄できます。他の2つのうちどちらを選ぶかは好みの問題です。どちらがより可能性が高いと思いますか?誰かが顧客以外の人に渡し、add_customer物事が壊れる(この場合、バージョン3が推奨されます)、または誰かがいつか、顧客を適切なメソッドのすべてに応答するある種のプロキシオブジェクトに置き換えることを望むこと(この場合、バージョン1が推奨されます)。

個人的に私は両方の故障モードを見てきました。私は怠惰であり、タイピングが少ないという一般原則の外に、バージョン1を採用する傾向があります。(また、この種の障害は、通常、遅かれ早かれかなり明白な方法で現れる傾向があります。そして、プロキシオブジェクトを使用したい場合、手をつないだ人に本当にイライラします。)しかし、尊敬するプログラマーがいます逆に行きます。


特にインターフェースを設計するときは、v.3を好む-新しいクラスとメソッドを書く。また、私のコードは他の人にとって新しいので、APIメソッドにv.3が役立つと考えています。アサーティブなアプローチは、最適化モードで実行すると本番環境で削除されるため、良い妥協案だと思います。>それを爆破することは、誰かが合理的なことをするのを止めることよりも、タイプミスをキャッチする可能性が高くなります。<それで、あなたはそのような検証をしても構わないのですか?
warvariuc 2013年

このようにしましょう。継承は、デザインをどのように進化させたいかを適切にマップしていないことがわかりました。作曲が好きです。だから私はこれがそのクラスでなければならないという主張を遠慮します。しかし、私は彼らが私を救うと思うアサーションに反対していません。
btilly 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.