* argsおよび** kwargsの型注釈


158

いくつかのインターフェイスを作成するために、抽象基本クラスを使用してPythonの型注釈を試しています。*argsandの可能なタイプに注釈を付ける方法はあり**kwargsますか?

たとえば、関数への賢明な引数が1つintまたは2つintのであることをどのように表現しますか?type(args)与えTuple私の推測のようにタイプに注釈を付けることだったのでUnion[Tuple[int, int], Tuple[int]]、これは動作しません。

from typing import Union, Tuple

def foo(*args: Union[Tuple[int, int], Tuple[int]]):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

# ok
print(foo((1,)))
print(foo((1, 2)))
# mypy does not like this
print(foo(1))
print(foo(1, 2))

mypyからのエラーメッセージ:

t.py: note: In function "foo":
t.py:6: error: Unsupported operand types for + ("tuple" and "Union[Tuple[int, int], Tuple[int]]")
t.py: note: At top level:
t.py:12: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:14: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 2 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"

mypyはtuple、呼び出し自体にaがあることを期待しているため、関数呼び出しではこれを好まないのは理にかなっています。解凍後の追加も、理解できない入力エラーを引き起こします。

*argsand の賢明な型にどのように注釈を付けるの**kwargsですか?

回答:


167

可変位置引数(*args)および可変キーワード引数(**kw)の場合、指定する必要があるのは、そのような引数の1つに期待される値だけです。

タイプヒント PEP の任意の引数リストとデフォルトの引数値セクションから:

任意の引数リストには、タイプ注釈を付けることもできるため、定義は次のようになります。

def foo(*args: str, **kwds: int): ...

は受け入れ可能であり、たとえば、以下のすべてが有効なタイプの引数を持つ関数呼び出しを表すことを意味します。

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

したがって、次のようにメソッドを指定する必要があります。

def foo(*args: int):

ただし、関数で1つまたは2つの整数値しか受け入れられない場合は、まったく使用*argsしないでください。1つの明示的な位置引数と2番目のキーワード引数を使用します。

def foo(first: int, second: Optional[int] = None):

これで、関数は実際には1つまたは2つの引数に制限され、指定する場合は両方とも整数でなければなりません。*args は常に 0以上を意味し、タイプヒントによって特定の範囲に制限することはできません。


1
好奇心旺盛ですが、なぜOptional?Pythonについて何か変化はありましたか、またはあなたの考えを変えましたか?Noneデフォルトのため、それはまだ厳密に必要ではありませんか?
Praxeolitic 2017

10
@Praxeoliticはい、実際には、デフォルト値としてOptional使用するときの自動的な暗黙の注釈Noneにより、特定のユースケースが困難になり、現在はPEPから削除されています。
Martijn Pieters

5
これは興味のある人のためにこれ議論するリンクです。Optional将来的にエクスプリシットが必要になるように思えます。
リックはモニカ

これは実際にはCallableでサポートされていません:github.com/python/mypy/issues/5876
Shah

1
@ShitalShah:それは本当にその問題が何であるかではありません。Callableサポートされていない任意のタイプのためのヒントの言及*argsまたは**kwargs 完全な停止を。その特定の問題は、特定の引数に加えて他の任意の数を受け入れる呼び出し可能オブジェクトをマークアップすることに関するものであり*args: Any, **kwargs: Any、2つのキャッチオールの非常に特定の型ヒントを使用します。より具体的な設定*argsや設定を行う場合は、を**kwargs使用できますProtocol
Martijn Pieters

26

これを行う適切な方法は、 @overload

from typing import overload

@overload
def foo(arg1: int, arg2: int) -> int:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))

@overload実際の実装には注釈を追加または入力しないでください。注釈は最後に来る必要があります。

スタブファイルの外でtyping @overloadをサポートするには、両方の新しいバージョンとmypy が必要です

これを使用して、返される結果を変化させ、どの引数タイプがどの戻りタイプに対応するかを明示することもできます。例えば:

from typing import Tuple, overload

@overload
def foo(arg1: int, arg2: int) -> Tuple[int, int]:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return j, i
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))

2
この回答は、より一般的なケースに対応しているため、気に入っています。振り返ってみると、例として(type1)vs (type1, type1)関数呼び出しを使用するべきではありませんでした。たぶん(type1)vs (type2, type1)はより良い例であり、私がこの答えを好きな理由を示しています。これにより、異なる戻り値の型も許可されます。ただし、戻り値の型が1つだけで、*argsかつ*kwargsがすべて同じ型であるという特殊なケースでは、マートジンの回答の手法の方が理にかなっており、両方の回答が役立ちます。
Praxeolitic 2017

4
*argsただし、引数の最大数(ここでは2つ)がある場所を使用することはまだ間違っています。
Martijn Pieters

1
@MartijnPietersなぜ*argsここで必ずしも間違っているのですか?予想される呼び出しが(type1)vs (type2, type1)である場合、引数の数は可変であり、後続の引数に適切なデフォルトはありません。最大数があることが重要なのはなぜですか?
Praxeolitic 2017

1
*argsゼロ以上の、上限なしの、同種の引数、または「手付かずのキャッチオールにこれらを渡す」ために実際に存在します。必須の引数が1つとオプションの引数が1つあります。これはまったく異なり、通常、2番目の引数に省略されていることを検出するための番兵のデフォルト値を与えることによって処理されます。
Martijn Pieters

3
PEPを見ると、これは明らかに@overloadの使用目的ではありません。この回答は、のタイプに個別に注釈を付ける興味深い方法を示し*argsていますが、質問に対するより良い回答は、これはまったく行うべきものではないということです。
Praxeolitic 2017

20

前の回答への短い追加として、Python 2ファイルでmypyを使用しようとしていて、注釈の代わりにコメントを使用して型を追加する必要がある場合は、型の接頭辞としてargs、およびkwargswith *およびwith を**それぞれ使用する必要があります。

def foo(param, *args, **kwargs):
    # type: (bool, *str, **int) -> None
    pass

これは、以下のPython 3.5バージョンと同じものとしてmypyによって処理されますfoo

def foo(param: bool, *args: str, **kwargs: int) -> None:
    pass
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.