1. ホーム
  2. python

[解決済み] ラムダ関数をタイプヒントすることは可能ですか?

2022-05-24 11:50:24

質問

現在、Pythonでは、関数のパラメータと戻り値の型は以下のようにタイプヒントを出すことができます。

def func(var1: str, var2: str) -> int:
    return var1.index(var2)

これは、この関数が2つの文字列を受け取り、整数を返すことを示しています。

しかし、この構文はラムダと非常に紛らわしく、ラムダは以下のように見えます。

func = lambda var1, var2: var1.index(var2)

パラメータと戻り値の型の両方に型ヒントを入れてみたのですが、構文エラーにならない方法がわかりません。

ラムダ関数をタイプヒントすることは可能ですか? そうでない場合、ラムダをタイプヒントする計画はありますか、または(明白な構文の衝突以外の)そうしない理由は何ですか?

どのように解決するのですか?

Python 3.6 以降では PEP 526 変数アノテーション . を代入した変数にアノテーションを付けることができます。 lambda の結果を代入する変数に typing.Callable 汎用 :

from typing import Callable

func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)

これは関数オブジェクト自体には型ヒンティング情報を付けず、オブジェクトを格納した名前空間のみに付けますが、通常、型ヒンティングの目的にはこれだけでよいでしょう。

しかし、代わりに関数ステートメントを使用することもできます。 lambda が提供する唯一の利点は、単純な式のための関数定義を置くことができることです。 の中に の中に入れることができるということです。しかし、上記のラムダはより大きな式の一部ではなく、あくまでも名前に束縛された代入文の一部なのです。それはまさに def func(var1: str, var2: str): return var1.index(var2) 文が実現するものです。

アノテーションを付けることができないことに注意してください。 *args または **kwargs のドキュメントにあるように、別々に引数を取ることもできます。 Callable のドキュメントに記載されています。

オプションまたはキーワード引数を示す構文はありません。そのような関数型がコールバック型として使用されることはほとんどありません。

その制限は PEP 544 プロトコル を持つ __call__ メソッド を使用します。どのような引数を受け付けるかを表現豊かに定義する必要がある場合に、これを使用します。Python 3.8が必要です。 または をインストールしてください。 typing-extensions プロジェクト でバックポートしてください。

from typing_extensions import Protocol

class SomeCallableConvention(Protocol):
    def __call__(self, var1: str, var2: str, spam: str = "ham") -> int:
        ...

func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam

については lambda 表現 自体 のように、アノテーション(Pythonの型ヒンティングが構築されている構文)は一切使えません。この構文は def 関数文でのみ利用可能です。

から PEP 3107 - 関数アノテーション :

lambdaの構文はアノテーションをサポートしていません。lambdaの構文は、パラメータリストの周りに括弧を必要とすることで、アノテーションをサポートするように変更することができます。しかし、それは は決定された という理由で、この変更を行わないことにした。

  • 互換性のない変更になります。
  • どうせラムダは去勢される。
  • ラムダはいつでも関数に変更できる。

アノテーションを直接オブジェクトにアタッチすることも可能で、その場合は function.__annotations__ 属性は書き込み可能な辞書です。

>>> def func(var1: str, var2: str) -> int:
...     return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}

もちろん、このような動的アノテーションが、型ヒントの上で静的アナライザーを走らせたいときに役立つというわけではありません。