ラムダ関数のヒントを入力することは可能ですか?


83

現在、Pythonでは、関数のパラメーターと戻り値の型は、次のように型ヒントを付けることができます。

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

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

ただし、この構文は、次のようなラムダと非常に混同されます。

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

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

ラムダ関数のヒントを入力することは可能ですか?そうでない場合は、型ヒントラムダの計画がありますか、または何らかの理由(明らかな構文の競合以外)はありませんか?


ラムダの通常のユースケースは、別の関数内にネストされていると思います(1つの場所で定義され、まったく異なる場所で呼び出される通常の関数とは対照的です)。その関数の型をすでに知っている場合は、(原則として)ラムダが受け取る型を理解するのは非常に簡単です。さらに、アノテーションをサポートするように構文を変更すると、ラムダが読みにくくなります。
mgilson 2015年

なぜあなたはそれをしたいのですか?ラムダ構文は、たとえば組み込みのkey引数として、非常に制約のあるコンテキスト内で使用するための「使い捨て」関数用sortedです。このような限られたコンテキストで型ヒントを追加しても、実際には意味がありません。さらに、PEP-526変数注釈を使用して型ヒントを追加しlambda、要点であるIMHOを完全に見逃しています。lambda構文が定義することを意図された匿名関数を。使用lambdaしてすぐに変数にバインドする意味は何ですか?使用するだけdefです!
ルシアーノラマーリョ

回答:


90

Python 3.6以降では、PEP526変数アノテーションを使用できます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

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

この制限はメソッドを使用するPEP544プロトコルに__call__は適用されません。どの引数を受け入れるべきかを明確に定義する必要がある場合は、これを使用してください。Python 3.8が必要である、バックポート用にtyping-extensionsプロジェクトをインストールします

from typing-extensions import Protocol

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

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

lambda表現自体は、任意の注釈(Pythonのタイプヒンティングが構築されているシンタックス)を使用することはできません。構文は、def関数ステートメントでのみ使用できます。

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

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

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

注釈をオブジェクトに直接添付することもでき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'>}

もちろん、タイプヒントに対して静的アナライザーを実行したい場合は、このような動的アノテーションが役立つわけではありません。


1
答えがfunc: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)それならなぜより良い答えではないのですdef func(var1: str, var2: str) -> int: return var1.index(var2)
グイドヴァンロッサム

@GuidovanRossum:それが別の質問への答えだからです。:-)ここでの質問は、関数の定義と同じ結果をもたらすラムダに型ヒントを適用する方法でした。それでも、ラムダを使用したくない理由を説明するセクションを追加します。
MartijnPieters

1
いいえ、文字通り答えは「いいえ、不可能です。これを変更する予定はありません。理由は主に構文上のものです。名前付き関数を定義して注釈を付けるのは簡単です。」また、ラムダが大きな呼び出しまたはデータ構造内に埋め込まれている場合、提案された回避策(特定のCallable型で変数を作成する)はあまり役に立たないため、関数を定義するよりも「優れた」ものではないことにも注意してください。(Callable表記はあまり読みにくいので、もっと悪いと思います。)
Guido van Rossum

このソリューションは、存続期間中に異なるラムダを割り当てることができる特定のCallable型で変数を定義しているように見えることによっても気が散ります。これは、関数を定義することとは異なり(defを再定義または再割り当てした場合、少なくともmypyはボークしますが、Callable型の変数は割り当てません)、元の質問「型注釈付きのラムダが必要です」からさらに離れています。この場合、def形式の代わりにラムダ形式を保持することに利点はないと私は主張し続けます。(そして、タイプの注釈がなかったとしても、私は言うでしょう。)
Guido van Rossum

1
@GuidovanRossum:では、あなた自身の答えを書いてください。言語の父としてのあなたの特定の地位で、あなたによる答えは、与えられた時間で、これを簡単に上回り、あなたはそれにあるべきだと思うものを正確に書くことができます。
MartijnPieters

43

Python 3.6以降、次のことができます(PEP 526を参照)。

from typing import Callable
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)

私はこの答えを理解していません:あなたはどこでタイプを設定しますxか?
stenci

3
@stenciis_even関数はCallable1つのint引数を期待する関数なので、xはint。です。
2018年

今、私は分かる。一見したところ、ラムダによって返される型のみに注釈を付けており、引数の型には注釈を付けていないと思いました。
stenci

4
これは実際にはラムダ自体に注釈を付けているわけではありません。注釈付き関数の場合のように、ラムダオブジェクトからこれらの注釈を取得することはできません
cz 2018年

3
mypy0.701とPython3.7は、これを正しくタイプチェックしis_even('x')ます。タイプエラーが発生します。
コンラッドルドルフ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.