Pythonで関数の引数をチェックする最良の方法
pythonの関数の変数をチェックする効率的な方法を探しています。例えば、引数の型と値をチェックしたいのです。このためのモジュールはあるのでしょうか?それとも、デコレータのようなもの、あるいは特定のイディオムを使うべきでしょうか?
def my_function(a, b, c):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
56
14
この細長い答えで。, Python 3.x固有のタイプチェックデコレーターを実装しています。 [PEP 484。]https://www.python.org/dev/peps/pep-0484。)-スタイルタイプは、275行未満の純粋なPythonを示唆しています。 (そのほとんどは説明的な説明とコメントです。) –産業強度の現実世界での使用向けに大幅に最適化されています。 [
py.test
。]https://github.com/pytest-dev/pytest。)-すべての可能なエッジケースを実行する駆動テストスイート。bear typing の予想外の素晴らしいものをごちそう:
この例が示唆するように、クマのタイピングはパラメータのタイプチェックと、そのようなタイプの単純なタイプまたはタプルのいずれかとして注釈が付けられた戻り値を明示的にサポートしています。 うれしい。!。
OK、それは実際には印象的ではありません。
@beartype
は、PEP 484スタイルのタイプに基づくすべてのother Python 3.x固有のタイプチェックデコレーターに似ています。純粋なPythonの275行。 だから、摩 ⁇ は何ですか、バブ?純粋なブルートフォースハードコア効率。
クマのタイピングは、Pythonでのタイプチェックの既存のすべての実装よりも、限られたドメインの知識の中で、空間と時間の両方で劇的に効率的です。 (後で詳しく説明します。)。
ただし、Pythonでは通常、効率は関係ありません。 もしそうなら、あなたはPythonを使用しないでしょう。 タイプチェックは実際には、Pythonでの早期最適化を回避するという確立された基準から逸脱していますか?? はい。 はい、そうです。。
プロファイリングを検討してください。これにより、プロファイリングされた各関心のある指標に不可避のオーバーヘッドが追加されます(例:.、関数呼び出し、行)。 正確な結果を確実にするために、このオーバーヘッドは、最適化されたC拡張機能(例:.、最適化されていない純粋なPythonではなく、「cProfile」モジュールによって活用される「_lsprof」C拡張子(例:.、
プロファイル
モジュール)。 プロファイリング時に効率は本当に重要です。タイプチェックも例外ではありません。 タイプチェックは、アプリケーションによってチェックされた各関数呼び出しタイプにオーバーヘッドを追加します。理想的には、それらの_all_です。 先週の金曜日のカフェインを追加した徹夜で老人レガシーのDjango Webアプリに追加した後、意味のある(しかし悲しいことに小柄な)同僚が静かに追加したタイプチェックを削除できないようにするには、タイプチェックを高速にする必要があります。非常に高速なので、誰にも言わずに追加しても、そこに誰も気付かないでしょう。 私はいつもこれをします。! 同僚の場合は、これを読むのをやめてください。< / sup>。
ただし、滑 ⁇ な速度でさえ大食いアプリケーションに十分でない場合、Pythonの最適化を有効にすることで、クマのタイピングがグローバルに無効になることがあります(例:.、
-O
オプションをPythonインタープリターに渡すことにより):ただ。 クマのタイピングへようこそ。
なに。..? なぜ「クマ」? あなたは首ひげです?
クマのタイピングは裸金属タイプのチェックです。つまり、可能な限りPythonでのタイプチェックの手動アプローチに近いタイプチェックです。 ベアタイピングは、_no_パフォーマンスのペナルティ、互換性の制約、またはサードパーティの依存関係(とにかく、手動アプローチによって課されるもの以上)を課すことを目的としています。 クマのタイピングは、変更することなく、既存のコードベースとテストスイートにシームレスに統合できます。
誰もがおそらく手動のアプローチに精通しています。 コードベースの_every_関数に渡された、および/または返された値に渡された各パラメーターを手動で「アサート」します。 ボイラープレートは、より単純またはより平凡である可能性があります? 私たちは皆、それをグーグルプレックスの100倍見て、毎回口の中で少し吐きました。 繰り返しは早く古くなります。 DRY、よ。
⁇ 吐バッグを準備してください。 簡潔にするために、単一の「str」パラメーターのみを受け入れる簡略化された「easy_spirit_bear()」関数を想定します。 手動アプローチは次のようになります。
Python 101、そうです? 私たちの多くはそのクラスを通過しました。
ベアタイピングは、上記のアプローチによって手動で実行されたタイプチェックを、同じチェックを自動的に実行する動的に定義されたラッパー関数に抽出します。あいまいな「AssertionError」例外ではなく、粒状の「TypeError」を発生させるという追加の利点があります。 自動化されたアプローチは次のとおりです。
長続きします。 しかし、それは基本的に *< / sup>です。手動アプローチと同じくらい速い。 \ * Squintingが推奨されます。< / sup>。
ラッパー機能での機能検査または反復の完全な欠如に注意してください。, これには、追加機能ではありますが、元の関数と同様の数のテストが含まれています。 (多分無視できます。) 型チェックするパラメーターが現在の関数呼び出しに渡されるかどうか、およびどのように渡されるかをテストするコスト。 すべての戦いに勝つことはできません。
このようなラッパー関数を確実に出力して、275行未満の純粋なPythonで任意の関数を型チェックできるようにできますか?? Snake Plisskinは、 "本当の話。 煙が出た?"。
そして、はい。 首ひげがあるかもしれません。
いいえ、Srsly。 なぜ「クマ」?
クマはアヒルを倒します。 アヒルは飛ぶかもしれませんが、クマはアヒルにサケを投げるかもしれません。 カナダでは、自然があなたを驚かせる可能性があります。< / sup>。
次の質問。
とにかく、クマの何がそんなに暑いのか?
既存のソリューションは、裸金属タイプのチェックを実行しません。少なくとも、私がグレッピングしたものはありません。 これらはすべて、各関数呼び出しで型チェック関数の署名を繰り返し再検査します。 1回の呼び出しでは無視できますが、すべての呼び出しで集約された場合、再検査オーバーヘッドは通常無視できません。 _本当に、本当に_無視できない。
ただし、これは単に効率の問題ではありません。 既存のソリューションでは、一般的なエッジケースを説明できないことがよくあります。 これには、ここや他の場所でstackoverflowの回答として提供される、すべてではないにしてもほとんどのおもちゃのデコレータが含まれます。 古典的な失敗は次のとおりです。
*チェックキーワードの引数や戻り値の入力に失敗(例:.、sweeneyrodの
@checkargs
decorator)。*タプルをサポートできません(つまり、.、ユニオン)
isinstance()
ビルトインによって受け入れられるタイプの。*名前、docstring、およびその他の識別メタデータを元の関数からラッパー関数に伝 ⁇ できません。
*少なくとも一連のユニットテストを提供できません。 (キンドオブクリティカル。)。
*失敗したタイプチェックで特定の「TypeError」例外ではなく、一般的な「AssertionError」例外を発生させます。 粒度と正気については、タイプチェックは一般的な例外を_決して_上げるべきではありません。
クマのタイピングは、非クマが失敗すると成功します。 すべて1つ、すべてクマ。!
ベアタイピングアンバレド。
クマのタイピングは、関数シグネチャを検査するためのスペースと時間のコストを関数呼び出し時間から関数定義時間にシフトします。つまり、「@ beartype」デコレーターによって返されるラッパー関数からデコレーター自体にシフトします。 デコレータは関数定義ごとに1回しか呼び出されないため、この最適化はすべての人に喜びをもたらします。
クマのタイピングは、タイプチェックケーキを食べさせようとする試みでもあります。 これを行うには、
@ beartype
:1。 元の関数の署名と注釈を検査します。
1。 元の機能をチェックするラッパー関数タイプの本体を動的に構築します。 そうだね。 Pythonコードを生成するPythonコード。
1。
exec()
ビルトインを介してこのラッパー関数を動的に宣言します。1。 このラッパー関数を返します。
しましょう? ディープエンドに飛び込みましょう。
そしてleycecは言った、「@ beartype」にタイプチェックをすばやく行わせてください。
キャベツ、呪い、空の約束。
完璧なものはありません。 _クマも入力しています。
Caveat I:デフォルト値のチェックが解除されました。
ベアタイピングは、デフォルト値が割り当てられた非パスパラメーターを_not_タイプチェックします。 理論的には可能です。 しかし、275行以下ではなく、スタックオーバーフローの答えではありません。
金庫(。..おそらく完全に安全ではない)仮定は、関数実装者がデフォルト値を定義したときに何をしていたかを知っていると主張することです。 デフォルト値は通常定数(。..彼らはより良いでしょう。!)、割り当てられた1つ以上のデフォルト値で各関数呼び出しで決して変化しない定数のタイプを再チェックすると、クマのタイピングの基本的な信条に違反します。「自分自身を繰り返さないでください。"。
間違って見せてください。賛成票でシャワーを浴びます。
Caveat II:PEP 484はありません。
PEP 484( "Type Hints" )は、[PEP 3107](https:/ /www.python.org/dev/peps/pep-3107/unction ". Python 3.5は、この形式化を新しいトップレベル
typing
moduleで表面的にサポートしています。これは、単純なタイプから任意に複雑なタイプを構成するための標準APIです。 (例えば.、Callable [[Arg1Type、Arg2Type]、ReturnType]
、タイプArg1Type
とArg2Type
の2つの引数を受け入れ、タイプReturnType
の値を返す関数を説明するタイプ)。クマのタイピングはそれらのどれもサポートしていません。 理論的には可能です。 しかし、275行以下ではなく、スタックオーバーフローの答えではありません。
ただし、クマのタイピングは、「isinstance()」組み込みがタイプのユニオンをサポートするのと同じ方法でタイプのユニオンをサポートします。**これは表面的には「typing.Union」タイプに対応します。「typing.Union」は任意に複雑なタイプをサポートするという明らかな警告があり、「@ beartype」で受け入れられるタプルは_only_単純なクラスをサポートします。 私の防御では、275ライン。
テストまたはそれは起こりませんでした。
これがgistです。 入手してください、 gist? もうやめます。< / sup>。
@ beartype
デコレータ自体と同様に、これらのpy.test
テストは、変更せずに既存のテストスイートにシームレスに統合できます。 貴重ですね?今、必須の首ひげは誰も求めなかった。
API暴力の歴史。
Python 3.5は、PEP 484タイプの使用を実際にサポートしていません。 ワット?。
それは本当です:タイプチェック、タイプ推論、タイプnuthin 'はありません。 代わりに、開発者は、そのようなサポートのファクシミリを実装するヘビー級のサードパーティCPythonインタープリターラッパーを介して、コードベース全体を定期的に実行することが期待されます(例:.、mypy)。 もちろん、これらのラッパーは以下を課します。
A 互換性のペナルティ。 公式mypy FAQとして)よくある質問への回答を認めています「mypyを使用して、既存のPythonコードをタイプチェックできますか?":" 場合によります。互換性はかなり優れていますが、一部のPython機能はまだ実装されていないか、完全にサポートされていません。"後続のFAQ応答は、次のように述べてこの非互換性を明確にします。
*「Mypyはモジュール式の効率的なタイプチェックをサポートします。これにより、メソッドの任意のランタイム追加など、一部の言語機能をタイプチェックから除外するようです。 ただし、これらの機能の多くは制限された形式でサポートされる可能性があります(たとえば、ランタイムの変更は、動的または「パッチ可能」として登録されたクラスまたはメソッドでのみサポートされます)。"。
*構文の非互換性の完全なリストについては、 "一般的な問題への対処" を参照してください。 きれいじゃない。 タイプチェックが必要なだけで、コードベース全体を作り直し、候補者のリリースから2日間、全員のビルドを破りました。カジュアルなビジネス服装の居心地の良いHRミゼットは、キュービクル兼マンケーブの亀裂にピンクのスリップを入れます。 どうもありがとう、mypy。
パフォーマンスのペナルティ静的に入力されたコードを解釈しても。 ハードボイルドコンピュータサイエンスの40年は、(。..他のすべては等しい)静的に入力されたコードの解釈は、動的に入力されたコードの解釈よりも速く、遅くはないはずです。 Pythonでは、upが新しいダウンです。
*追加の非重要な依存関係、増加:
*バグが多いプロジェクト展開の脆弱性、特にクロスプラットフォーム。
*プロジェクト開発の維持管理負担。
*攻撃面の可能性。
私はグイドに尋ねます:「なぜ? あなたが実際にその抽象化で何かをしている具体的なAPIをポニーアップするつもりがなかったのに、なぜ抽象APIを発明するのか?「100万人のPythonistasの運命を無料のオープンソースマーケットの関節炎の手に任せるのはなぜですか? 公式のPython stdlibの275行の装飾機で簡単に解決できた可能性のある、さらに別のテクノ問題を作成する理由?
私にはPythonがなく、悲鳴を上げる必要があります。
最もPythonicのイディオムは、関数が期待することを明確に_document_し、関数に渡されたものをすべて使用して、例外を伝 ⁇ させるか、属性エラーをキャッチして代わりに「TypeError」を発生させることです。 タイプチェックは、ダックタイピングに反するため、できるだけ避ける必要があります。 コンテキストによっては、値のテストでも問題ありません。
検証が本当に意味のある唯一の場所は、Webフォーム、コマンドライン引数などのシステムまたはサブシステムのエントリポイントです。 それ以外の場合は、機能が適切に文書化されている限り、適切な議論を渡すのは発信者の責任です。
編集:2019年の時点で、Pythonで型注釈と静的チェックを使用するためのサポートがさらにあります。 typingモジュールとmypyをチェックしてください。 2013年の回答は次のとおりです。
----------。
タイプチェックは通常、Pythonicではありません。 Pythonでは、アヒルのタイピングを使用する方が一般的です。 例:
コードでは、引数(例では
a
)がint
のように歩き、int
のようにガクガクすると仮定します。 例:つまり、関数は整数で機能するだけでなく、フロートや「add」メソッドが定義されたユーザー定義のクラスでも動作するため、あなたや他の誰かが拡張したい場合は、何もする必要はありません(場合によっては何もする必要はありません)。他の何かで作業する機能。 ただし、「int」が必要になる場合があるため、次のようなことができます。
関数は、
__int__
メソッドを定義する任意のa
で引き続き機能します。他の質問に答えて、私はそれが最善だと思います(他の答えがこれを行うために言ったので:
または。
私が作ったいくつかのタイプチェックデコレーター:
一つの方法として、
assert
を使用する方法があります:Type Enforcement accept / returnsデコレータを使用できます。 PythonDecoratorLibrary。 それは非常に簡単で読みやすいです:
Pythonの変数が何であるかをチェックする方法はいくつかあります。 だから、いくつかリストするには:
-
isinstance(obj、type)
関数は変数obj
を取り、True
は、リストしたtype
と同じタイプです。-変数
obj
を取り込み、obj
がclass
のサブクラスである場合はTrue
を提供するissubclass(obj、class)
関数。 したがって、たとえば、「issubclass(Rabbit、Animal)」は「True」値になります。-
hasattr
は、この関数super_len
:で示される別の例です。---。
---。
hasattr
は、ダックタイピング、および通常は_pythonic_である何かに傾いていますが、その用語は意見が分かれています。注のように、「assert」ステートメントは通常テストで使用されます。それ以外の場合は、「if / else」ステートメントを使用します。
私は最近、そのトピックについてかなりの調査を行いました。なぜなら、私はそこで見つけた多くのライブラリに満足していなかったからです。
私はこれに対処するためのライブラリを開発することになりました、それはvalid8と名付けられました。 ドキュメントで説明されているように、それは主に値検証用です(ただし、単純なタイプ検証関数もバンドルされています)。[enforce](https:// github)などのPEP484ベースのタイプチェッカーに関連付けることをお勧めします。 .com / RussBaz / enforce)または[pytypes](https:/github.com/Stewori/typypes.
これは、検証ロジックを定義するために、実際に「valid8」のみ(および
mini_lambda
で検証を実行する方法です-しかし、それは必須ではありません)あなたの場合:これは、PEP484タイプのヒントを活用し、タイプチェックを「強化」に委任する同じ例です。
通常はこのようなことをします:
これは、関数:を呼び出すときに入力引数のタイプをチェックします。
また、
second = 9
で確認します(アサーションエラーを指定する必要があります)。デモ:
locals()の詳細。
複数の関数の検証を行う場合は、次のようにデコレータ内にロジックを追加できます。
そしてそれを使用:
これが役立つことを願っています。!
通常の引数だけでなく、
**kwargs
,*args
もまとめてチェックしたい場合は、関数定義の最初の文としてlocals()
関数を使用すると、引数の辞書を取得することができます。次に
type()
を使って、例えば dict を繰り返しながら引数を調べます。これは解決策ではありませんが、関数呼び出しを特定のパラメータータイプに制限する場合は、PROATOR {Python関数プロトタイプバリデーター}を使用する必要があります。 次のリンクを参照できます。 https://github.com/mohit-thakur-721/proator。