関数アノテーション: PEP-3107
Python3の関数アノテーションを示すコードスニペットを見つけました。概念は単純ですが、なぜこれらがPython3で実装されたのか、またはそれらの適切な使用が考えられません。おそらくSO私を啓発できますか?
使い方:
def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
... function body ...
引数の後のコロンに続くものはすべて「注釈」であり、->
に続く情報は関数の戻り値の注釈です。
foo.func_annotationsは辞書を返します。
{'a': 'x',
'b': 11,
'c': list,
'return': 9}
これを利用できることの意義は何ですか?
これは実際に素晴らしいと思います。
学歴から来て、私は注釈がJavaのような言語のためのスマートな静的アナライザーを可能にするためにそれ自身が非常に貴重であることを証明したと言うことができます。たとえば、状態の制限、アクセスが許可されているスレッド、アーキテクチャの制限などのセマンティクスを定義できます。これらを読み取って処理し、コンパイラから得られるものを超える保証を提供できるツールが多数あります。前提条件/事後条件をチェックするものを書くことさえできます。
タイピングが弱いため、Pythonでは特にこのようなものが必要であると感じていますが、これを直接的な公式構文の一部にする構成要素は実際にはありませんでした。
保証の範囲を超えて注釈には他の用途があります。 JavaベースのツールをPythonに適用する方法がわかります。たとえば、メソッドに特別な警告を割り当て、ドキュメントを読む必要があることを呼び出すときに指示を与えるツールがあります(たとえば、負の値で呼び出されてはならないメソッドがあると想像してくださいが、名前から直感的ではありません)。注釈を使用すると、Pythonでこのようなものを技術的に書くことができます。同様に、公式の構文があれば、タグに基づいて大きなクラスのメソッドを編成するツールを作成できます。
関数注釈は、あなたが作成したものです。
これらは文書化に使用できます。
def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
...
これらは、事前条件チェックに使用できます。
def validate(func, locals):
for var, test in func.__annotations__.items():
value = locals[var]
msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
assert test(value), msg
def is_int(x):
return isinstance(x, int)
def between(lo, hi):
def _between(x):
return lo <= x <= hi
return _between
def f(x: between(3, 10), y: is_int):
validate(f, locals())
print(x, y)
>>> f(0, 31.1)
Traceback (most recent call last):
...
AssertionError: Var: y Value: 31.1 Test: is_int
型チェックを実装する方法については、 http://www.python.org/dev/peps/pep-0362/ も参照してください。
私の答え here から適切な使用の具体例を追加するだけで、デコレータと組み合わせて、マルチメソッドの簡単なメカニズムを実行できます。
# This is in the 'mm' module
registry = {}
import inspect
class MultiMethod(object):
def __init__(self, name):
self.name = name
self.typemap = {}
def __call__(self, *args):
types = Tuple(arg.__class__ for arg in args) # a generator expression!
function = self.typemap.get(types)
if function is None:
raise TypeError("no match")
return function(*args)
def register(self, types, function):
if types in self.typemap:
raise TypeError("duplicate registration")
self.typemap[types] = function
def multimethod(function):
name = function.__name__
mm = registry.get(name)
if mm is None:
mm = registry[name] = MultiMethod(name)
spec = inspect.getfullargspec(function)
types = Tuple(spec.annotations[x] for x in spec.args)
mm.register(types, function)
return mm
および使用例:
from mm import multimethod
@multimethod
def foo(a: int):
return "an int"
@multimethod
def foo(a: int, b: str):
return "an int and a string"
if __== '__main__':
print("foo(1,'a') = {}".format(foo(1,'a')))
print("foo(7) = {}".format(foo(7)))
Guidoの元の投稿 が示すように、デコレータに型を追加することでこれを行うことができますが、パラメータと型の誤った一致の可能性を回避するため、パラメータ自体に注釈を付ける方が優れています。
注:Pythonでは、注釈にfunction.__annotations__
のではなく function.func_annotations
としてfunc_*
スタイルは、Python 3。
Uriはすでに適切な答えを出しているので、ここではそれほど深刻ではありません。したがって、docstringを短くすることができます。
注釈を初めて見たとき、「素晴らしい!最終的に何らかの型チェックを選択できるようになった!」と思いました。もちろん、私は注釈が実際に強制されないことに気づいていませんでした。
そこで、私は 単純な関数デコレータを作成してそれらを強制する :
def ensure_annotations(f):
from functools import wraps
from inspect import getcallargs
@wraps(f)
def wrapper(*args, **kwargs):
for arg, val in getcallargs(f, *args, **kwargs).items():
if arg in f.__annotations__:
templ = f.__annotations__[arg]
msg = "Argument {arg} to {f} does not match annotation type {t}"
Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
return_val = f(*args, **kwargs)
if 'return' in f.__annotations__:
templ = f.__annotations__['return']
msg = "Return value of {f} does not match annotation type {t}"
Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
return return_val
return wrapper
@ensure_annotations
def f(x: int, y: float) -> float:
return x+y
print(f(1, y=2.2))
>>> 3.2
print(f(1, y=2))
>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>
Ensure ライブラリに追加しました。
これが尋ねられてから長い時間が経ちましたが、質問に示されているスニペットの例は(同様に述べられているように)PEP 3107からのものであり、PESの例の最後にあります。見る ;)
以下はPEP3107から引用されています
ユースケース
注釈を議論する過程で、いくつかのユースケースが提起されました。これらのいくつかは、それらが伝える情報の種類ごとにグループ化されてここに表示されます。また、注釈を使用できる既存の製品およびパッケージの例も含まれています。
特定のポイント(およびその参照)の詳細については、 [〜#〜] pep [〜#〜] を参照してください。
Python 3.X(のみ)は、関数定義を一般化し、拡張機能で使用するために引数と戻り値にオブジェクト値の注釈を付けることもできます。
説明するMETAデータ、関数値についてより明確にするため。
注釈は、引数名の後、デフォルトの前に:value
として、引数リストの後に->value
としてコーディングされます。
これらは関数の__annotations__
属性に収集されますが、Python自体によって特別なものとして扱われません:
>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}
ソース:Python Pocket Reference、Fifth Edition
例:
typeannotations
モジュールは、Pythonコードの型チェックと型推論のためのツールのセットを提供します。また、関数とオブジェクトに注釈を付けるのに役立つタイプのセットを提供します。
これらのツールは、主にリンター、コード補完ライブラリ、IDEなどの静的アナライザーで使用されるように設計されています。さらに、実行時チェックを行うためのデコレータが提供されます。 Pythonでは、実行時の型チェックが常に良いアイデアとは限りませんが、非常に便利な場合があります。
https://github.com/ceronman/typeannotations
タイピングがより良いコードを書くのを助ける方法
入力すると、静的コード分析を実行して、コードを実稼働環境に送信する前に型エラーをキャッチし、明らかなバグを防ぐことができます。 mypyのようなツールがあり、ソフトウェアライフサイクルの一部としてツールボックスに追加できます。 mypyは、コードベースに対して部分的にまたは完全に実行することにより、正しいタイプをチェックできます。 mypyは、関数から値が返されたときにNone型をチェックするなどのバグを検出するのにも役立ちます。入力すると、コードがよりきれいになります。 docstringで型を指定するコメントを使用してコードを文書化する代わりに、パフォーマンスコストなしで型を使用できます。
きれいなPython:Python ISBN:ISBN-13(pbk):978-1-4842-4877-5
PEP 526-変数注釈の構文
少し遅れた回答として、私のパッケージのいくつか(marrow.script、WebCoreなど)は、可能な場合は注釈を使用して型キャストを宣言します(つまり、Webからの入力値の変換、ブール引数である引数の検出など)。引数の追加マークアップを実行するため。
Marrow Scriptは、任意の関数とクラスへの完全なコマンドラインインターフェイスを構築し、ドキュメント、キャスト、およびコールバックから派生したデフォルト値をアノテーションを介して定義し、デコレーターを使用して古いランタイムをサポートします。注釈を使用する私のライブラリはすべて、フォームをサポートしています。
any_string # documentation
any_callable # typecast / callback, not called if defaulting
(any_callable, any_string) # combination
AnnotationClass() # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …] # cooperative annotation
docstringまたは型キャスト関数の「ベア」サポートにより、注釈を認識する他のライブラリとの混合が容易になります。 (つまり、タイプキャストを使用するWebコントローラーがありますが、これもコマンドラインスクリプトとして公開されることがあります。)
編集して追加:また、検証のために開発時のアサーションを使用して TypeGuard パッケージを利用し始めました。利点:「最適化」を有効にして実行すると(-O
/PYTHONOPTIMIZE
env var)高価な(たとえば、再帰的な)チェックは省略されます。開発中にアプリを適切にテストしたため、本番環境ではチェックは不要です。
ここで説明するすべての用途にもかかわらず、注釈の強制可能な、そしておそらく最も強制的な使用は、 type hints のためです。
これは現在いかなる方法でも実施されていませんが、PEP 484から判断すると、Pythonの将来のバージョンでは、注釈の値としてタイプのみが許可されます。
引用 注釈の既存の使用はどうですか? :
最終的にはタイプヒントがアノテーションの唯一の用途になることを願っていますが、これには、Python 3.5でのタイピングモジュールの最初のロールアウト後の追加の議論と廃止期間が必要です。 PEPは、Python 3.6がリリースされるまで、暫定的な状態(PEP 411を参照)になります。考えられる最も速いスキームは、3.6の非型ヒントアノテーションのサイレント非推奨、3.7の完全非推奨、および宣言を導入しますPython 3.8。
私はまだ3.6で静かな非推奨を見たことはありませんが、これは代わりに3.7に非常にうまくバンプできます。
そのため、他の優れたユースケースがあるかもしれませんが、この制限がある場所で将来すべてを変更したくない場合は、タイプヒンティングのためだけにそれらを保持するのが最善です。