Pythonでの関数アノテーション
Python 3.0で導入された関数アノテーションは、関数パラメーターと戻り値に任意のメタデータを追加できる機能を追加します。 Python 3以降、関数アノテーションがPythonに正式に追加されました(PEP-3107)。主な目的は、メタデータを関数パラメーターと戻り値にリンクする標準的な方法を用意することでした。
関数アノテーションの基本
関数アノテーションの基本を理解しましょう-
-
関数アノテーションは、パラメーターと戻り値の両方で完全にオプションです。
-
関数アノテーションは、コンパイル時に関数のさまざまな部分を任意のPython式に関連付ける方法を提供します。
-
PEP-3107は、組み込み型であっても、いかなる種類の標準セマンティクスも導入しようとはしません。この作業はすべてサードパーティのライブラリに任されています。
構文
単純なパラメータの注釈
パラメータの注釈は次の形式を取ります-
def foo(x: expression, y: expression = 20): ….
一方、過剰なパラメータの注釈は-
です。def foo(**args: expression, **kwargs: expression): …..
ネストされたパラメーターの場合、注釈は常にパラメーターの名前の後に続き、最後の括弧までは続きません。ネストされたパラメータのすべてのパラメータに注釈を付ける必要はありません。
def foo(x1, y1: expression), (x2: expression, y2: expression)=(None, None)): ……
Pythonはアノテーション付きのセマンティクスを提供しないことを理解することが重要です。メタデータを関連付けるための優れた構文サポートと、それにアクセスする簡単な方法のみを提供します。また、注釈は必須ではありません。
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)
上記の例では、関数func()はx、y、zという3つのパラメーターを取り、最後にそれらの合計を出力します。最初の引数xは文字列「annotatingx」で注釈が付けられ、2番目の引数yは文字列「annotatingy」で注釈が付けられ、3番目の引数zはタイプintで注釈が付けられます。戻り値にはfloat型の注釈が付けられます。ここでは、戻り値に注釈を付けるための「->」構文を使用しています。
出力
>>> func(2,3,-4) 1 >>> func('Function','-','Annotation') Function-Annotation
上記では、func()を2回呼び出します。1回はint引数を使用し、もう1回は文字列引数を使用します。どちらの場合も、func()は正しいことを行い、アノテーションは単に無視されます。したがって、アノテーションは関数func()の実行に影響を与えないことがわかります。
関数アノテーションへのアクセス
すべての注釈は__annotations__と呼ばれる辞書に保存され、それ自体が関数の属性です-
>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z) >>> func.__annotations__ {'x': 'annotating x', 'y': 'annotating y', 'z': <class 'int'>, 'return': <class 'float'>}
前のコード例でわかるように、注釈は型付き宣言ではありませんが、確かにその目的で使用でき、以下に示すように、他のいくつかの言語で使用される型付け構文に似ています-
>>> def func(a: 'python', b: {'category: ' 'language'}) -> 'yep': pass >>> func.__annotations__ {'a': 'python', 'b': {'category: language'}, 'return': 'yep'} >>>
これらは任意の式です。つまり、任意の値を__annotations__ディクショナリに格納できます。ただし、値を格納する必要があることを除いて、Python自体にはあまり意味がありません。とはいえ、パラメーターと戻り値のタイプを定義することは、関数アノテーションの一般的な使用法です。
@no_type_checkデコレータ
アノテーションが型宣言であると想定するツールを使用しているのに、他の目的で使用したい場合は、標準の@no_type_checkデコレータを使用して、次のように関数をそのような処理から除外します-
>>> from typing import no_type_check >>> @no_type_check def func(a: 'python', b: {'category: ' 'language'}) -> 'yep': pass >>>
通常、注釈を使用するほとんどのツールには、注釈を使用するツールを認識する方法があるため、これは必要ありません。デコレータは、物事があいまいなコーナーケースを保護するためのものです。
関数デコレータへの入力としてのアノテーション
アノテーションはデコレータとうまく組み合わせることができます。これは、アノテーション値がデコレータに入力を提供するための優れた方法であり、デコレータによって生成されたラッパーは、アノテーションに意味を与えるコードを配置するのに適した場所だからです。
from functools import wraps def adapted(func): @wraps(func) def wrapper(**kwargs): final_args = {} for name, value in kwargs. items(): adapt = func.__annotations__.get(name) if adapt is not None: final_args[name] = adapt(value) else: final_args[name] = value result = func(**final_args) adapt = func.__annotations__.get('result') if adapt is not None: return adapt(result) return result return wrapper @adapted def func(a: int, b: repr) -> str: return a
したがって、適応されたデコレータは関数をラッパーで囲みます。このラッパーはキーワード引数のみを受け入れます。つまり、元の関数が位置引数を受け入れることができたとしても、名前で指定する必要があります。
関数がラップされると、ラッパーは関数のパラメーターアノテーションでアダプターを探し、引数を実際の関数に渡す前にそれらを適用します。
関数が戻ると、ラッパーは戻り値アダプターをチェックします。見つかった場合は、最終的に返す前に戻り値に適用します。
ここで起こっていることの意味を考えると、それらはかなり印象的です。パラメータを関数に渡したり、値を返したりすることの意味を実際に変更しました。
キーワード引数
場合によっては、メソッドの1つ以上のパラメーターは、selfの属性に割り当てることを除いて、処理を必要としません。デコレータとアノテーションを使用して、これを自動的に実行できますか?もちろんできます。
from functools import wraps def store_args(func): @wraps(func) def wrapper(self, **kwargs): for name, value in kwargs.items(): attrib = func.__annotations__.get(name) if attrib is True: attrib = name if isinstance(attrib, str): setattr(self, attrib, value) return func(self, **kwargs) return wrapper class A: @store_args def __init__(self, first: True, second: 'example'): pass a = A(first = 5, second = 6) assert a.first == 5 assert a.example == 6
-
Pythonのissubset()関数
この記事では、Python標準ライブラリで利用可能なissubset()関数の実装と使用法について学習します。 issubset()メソッドは、セットのすべての要素が別のセットに存在する場合(引数として渡される場合)はブール値のTrueを返し、それ以外の場合はブール値のFalseを返します。 下の図では、BはAのサブセットです。AとBが同一のセットである場合、AはBの適切なサブセットであることを意味します。これは、両方のセットに同じ要素が含まれていることを意味します。 構文 <set 1>.issubset(<set 2>) 戻り値 boolean True/
-
Intersection()関数Python
この記事では、任意のセットで実行できるintersection()関数について学習します。数学によると、共通部分とは、2つのセットから共通の要素を見つけることを意味します。 構文 <set name>.intersection(<set a1> <set a2> ……..) 戻り値 引数として渡されるセット内の共通要素。 例 set_1 = {'t','u','t','o','r','i','a','l&