1. ホーム
  2. パイソン

[解決済み】Python 3.5の型ヒントは何ですか?

2022-03-27 23:04:17

質問

Python 3.5で最も話題になった機能の1つが タイプヒント .

の例です。 タイプヒント に記載されています。 この記事 この一件 また、タイプヒントを責任を持って使用するように言及しています。どなたか、これらのヒントについて、またどのような場合に使用し、どのような場合に使用しないかについて、詳しく説明していただけませんか?

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

を読むことをお勧めします。 PEP 483 PEP 484 と視聴 本プレゼンテーション によって グイドー タイプヒンティングについて。

一言で言えば : タイプヒンティングは文字通り、その言葉の通りの意味です。使用しているオブジェクトの型をヒントにするのです。 .

によるものです。 ダイナミック Pythonの性質上 型の推論またはチェック を使用することは特に困難です。この事実は、開発者が書いていないコードで何が起こっているかを正確に理解することを難しくし、最も重要なことは、多くのIDEで見られる型チェックツール( PyCharm PyDev が思い浮かびますが、オブジェクトがどのようなタイプであるかのインジケータを持っていないという事実のために、制限されています。その結果、型を推測することに頼ることになり、(プレゼンテーションで言及されたように)成功率は50%程度です。


タイプヒンティングのプレゼンテーションから2つの重要なスライドを紹介します。

なぜタイプヒントなのか?

  1. タイプチェッカーを支援します。 オブジェクトにどのような型を持たせたいかをヒントにすることで、例えば期待されていない型のオブジェクトを渡している場合、タイプチェッカーは簡単にそれを検出することができます。
  2. ドキュメント作成に役立ちます。 あなたのコードを見る第三者は、どこで何が期待されているのか、つまり、それらを得ることなくそれを使用する方法を知ることができます。 TypeErrors .
  3. IDEがより正確で堅牢なツールを開発するのを支援します。 開発環境は、オブジェクトの型がわかっている場合、適切なメソッドを提案するのに適しています。IDE を使っている人は、このような経験をしたことがあるでしょう。 . と、オブジェクトに定義されていないメソッドや属性がポップアップ表示されることがあります。

なぜ静的型チェッカーを使うのですか?

  • バグの早期発見 : これは自明のことだと思います。
  • プロジェクトが大きくなればなるほど、必要になる : 繰り返しになりますが、理にかなっています。静的言語には、堅牢性と制御性があります。 動的言語にはないものです。アプリケーションが大きく複雑になればなるほど、(動的言語にはない)コントロールと予測可能性が高まります。 動作の面で)必要です。
  • 大規模なチームでは、すでに静的解析を実施している : これは最初の2点を検証しているのでしょう。

この小さな紹介の締めくくりとして : これは 任意 この機能は、静的型付けの利点を享受するために導入されたと私は理解しています。

あなたは一般的に しない を気にする必要はありませんし 間違いなく を使う必要はありません(特に、Pythonを補助的なスクリプト言語として使う場合)。として、大規模なプロジェクトを開発する際に役立つはずです。 必要な堅牢性、制御、追加のデバッグ機能を提供します。 .


mypyを使ったタイプヒンティング :

この答えをより完全なものにするために、ちょっとしたデモンストレーションが適していると思います。私は、以下のものを使用します。 mypy PEPで紹介されているType Hintsのインスピレーションとなったライブラリです。これは主に、この問題にぶつかり、何から始めたらいいのか悩んでいる人のために書かれたものです。

その前に、以下のことを再確認しておきます。 PEP 484 を強制するものではなく、単に関数 のアノテーションとガイドラインを提案しています。 なんと の型チェックを行うことができます/行う必要があります。関数にアノテーションを付けて Pythonはアノテーションを使用しないので、アノテーションがあってもスクリプトは実行されます。

とにかく、PEPに書かれているように、型のヒンティングは一般的に3つの形式を取るべきです。

  • 関数アノテーション( PEP 3107 ).
  • ビルトイン/ユーザーモジュール用のスタブファイル。
  • 特別 # type: type というコメントがあり、最初の2つの形式を補完する。(参照 変数アノテーションとは何ですか? のPython 3.6アップデートのため。 # type: type のコメント)

さらに、タイプヒントを併用することで、新しい typing モジュールで導入された Py3.5 . その中で、多くの(追加の) ABC (抽象ベースクラス)が定義されており、静的チェックに使用するヘルパー関数やデコレーターも定義されています。のほとんどのABCは collections.abc が含まれますが、その中に ジェネリック を定義することで)購読できるようになります。 __getitem__() メソッド) を使用します。

これらについてのより詳細な解説に興味のある方は mypy documentation は、非常にうまく書かれており、そのチェッカーの機能を実演/説明するコードサンプルが多数掲載されているので、一読の価値があります。

関数のアノテーションと特別なコメント。

まず、特殊なコメントを使用した場合の動作について、いくつか観察してみると面白い。特別な # type: type コメント は、変数の代入の際に、オブジェクトの型が直接推定できない場合に、その型を示すために追加することができます。単純な代入は 一般的に容易に推測できますが、リスト(内容に関して)のように推測できないものもあります。

の任意の派生物を使用したい場合 コンテナ で、そのコンテナの内容を指定する必要があります。 必須 を使用します。 ジェネリック の型は typing モジュールを使用します。 これらはインデックスをサポートしています。

# Generic List, supports indexing.
from typing import List

# In this case, the type is easily inferred as type: int.
i = 0

# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = []  # type: List[str]

# Appending an int to our list
# is statically not correct.
a.append(i)

# Appending a string is fine.
a.append("i")

print(a)  # [0, 'i']

これらのコマンドをファイルに追加し、インタープリタで実行すると、すべてがうまく動作して print(a) を印刷するだけです。 リストの内容 a . その # type のコメントは破棄されました。 追加的な意味を持たないプレーンなコメントとして扱われる .

これを mypy 一方、以下のようなレスポンスが得られます。

(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"

のリストであることを示す。 str オブジェクトを含むことはできません。 int これは、静的に言えば、健全なことです。これは、静的に見れば問題ありません。 a を追加するだけです。 str オブジェクトの内容の型を変えるか、あるいは a で直感的に実行される)。 List[Any] の後に Any からインポートされました。 typing ).

関数アノテーションは、以下のような形式で追加されます。 param_name : type は関数シグネチャの各パラメータの後に、 戻り値の型を指定するために -> type アノテーションはすべて、関数終了コロンの前にある __annotations__ 属性が、便利な辞書形式で用意されています。些細な例を挙げます。 typing モジュール)。

def annotated(x: int, y: str) -> bool:
    return x < y

annotated.__annotations__ 属性は以下の値を持つようになりました。

{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}

もし私たちがまったくの初心者であったり、Python 2.7 の概念に慣れていて、結果として TypeError の比較に潜んでいる annotated であれば、別の静的チェックを行うことで、エラーをキャッチし、手間を省くことができます。

(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")

中でも、無効な引数で関数を呼び出すと、それも捕捉されます。

annotated(20, 20)

# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"

これらは基本的にあらゆるユースケースに拡張でき、キャッチされるエラーは基本的な呼び出しや操作よりもさらに拡張できる。検出されるエラーの種類は 今回は、その可能性をほんの少し紹介しただけです。の中を覗いてみてください。 typing モジュールは PEPs または mypy のドキュメントを参照すると、提供される機能についてより包括的なアイデアを得ることができます。

スタブファイル

スタブファイルは、相互に排他的でない2つの異なるケースで使用することができます。

  • 関数のシグネチャを直接変更したくないモジュールをタイプチェックする必要がある場合。
  • モジュールを書いてタイプチェックをしたいが、さらにアノテーションとコンテンツを分離したい。

どのようなスタブファイル(拡張子が .pyi は、あなたが作ろうとしている/使おうとしているモジュールの注釈付きインターフェイスです。その中には 型チェックをしたい関数のシグネチャと、その関数の本体を破棄したものです。これを理解するために、一組の という名前のモジュールに含まれる3つのランダムな関数の randfunc.py :

def message(s):
    print(s)

def alterContents(myIterable):
    return [i for i in myIterable if i % 2 == 0]

def combine(messageFunc, itFunc):
    messageFunc("Printing the Iterable")
    a = alterContents(range(1, 20))
    return set(a)

スタブファイルを作成することができます randfunc.pyi その中で、もしそうしたいのであれば、いくつかの制限を設けることができます。欠点は スタブなしでソースを見た人は、何がどうなっているのかを理解するための注釈を得ることはできないでしょう。 が渡される。

とにかく、スタブファイルの構造はとても単純です。空のボディを持つすべての関数定義を追加する ( pass を充填し アノテーションを追加してください。ここでは、以下のものだけを扱うとします。 int の型をコンテナに使用します。

# Stub for randfucn.py
from typing import Iterable, List, Set, Callable

def message(s: str) -> None: pass

def alterContents(myIterable: Iterable[int])-> List[int]: pass

def combine(
    messageFunc: Callable[[str], Any],
    itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass

combine この関数は、アノテーションを別のファイルで使用したい理由を示しています。 コードと可読性を低下させます(Pythonの大きな欠点です)。もちろん、型のエイリアスを使うこともできますが、それは時に、より混乱させることになります。 ので、賢明に使用してください)。


これで、Pythonの型ヒントの基本的な概念に慣れたはずです。使用するタイプチェッカーが mypy は、徐々に多くのポップアップを見るようになるはずで、IDE の内部 ( PyCharm などをPythonの標準モジュールとして提供しています。

今後、チェッカーや関連パッケージが見つかったら(あるいは提案されたら)、以下のリストに追加していこうと思います。

私が知っているチェッカー :

  • マイピー : ここに記載されているとおりです。
  • PyType : Googleによるもので、私が調べたところでは異なる記法を使用しているようですが、おそらく見る価値があるでしょう。

関連パッケージ/プロジェクト :

  • タイプシェッド Pythonの公式リポジトリで、標準ライブラリのスタブファイルの集合を収容しています。

typeshed プロジェクトは、型ヒンティングを自分のプロジェクトでどのように使用するかを見るのに最も適した場所の一つです。例として その __init__ のダントツ1位です。 Counter クラス に、対応する .pyi ファイルを作成します。

class Counter(Dict[_T, int], Generic[_T]):
        @overload
        def __init__(self) -> None: ...
        @overload
        def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
        @overload
        def __init__(self, iterable: Iterable[_T]) -> None: ...

どこ _T = TypeVar('_T') はジェネリッククラスを定義するために使用されます . については Counter クラスは、イニシャライザーで引数を取らないか、単一の Mapping を任意の型から int または を取る Iterable を任意の型に変換することができます。


お知らせ : 一つ言い忘れていたのは typing モジュールが導入されたのは 暫定版 . から PEP 411 :

暫定パッケージは、quot;安定版 (stable") に移行する前に API が変更される可能性があります。一方では、この状態は Python ディストリビューションの正式な一部であるという利点をパッケージに与えます。一方で、コア開発チームはパッケージの API の安定性に関して約束はしていないと明言しており、次のリリースでは変更される可能性があります。可能性は低いと思われますが、APIやメンテナンスに関する懸念に十分な根拠があることが証明された場合、そのようなパッケージは非推奨期間なしに標準ライブラリから削除される可能性さえあります。

削除されたり、大幅に変更されたりすることはないだろうが、わからないこともある。


** 全く別の話題ですが、タイプヒントの範囲では有効です。 PEP 526 : 変数アノテーションのシンタックス を置き換える試みです。 # type この構文により、ユーザーは変数の種類を単純な varname: type ステートメントを使用します。

参照 変数アノテーションとは何ですか? は、前述したように、これらについて少し紹介します。