327

Python 3.5 で最も話題になっている機能の 1 つは、型ヒントです。

タイプ ヒントの例は、この記事この記事で言及されていますが、責任を持ってタイプ ヒントを使用することについても言及されています。誰かがそれらについて、またいつ使用すべきか、いつ使用すべきでないかを説明できますか?

4

5 に答える 5

431

PEP 483PEP 484を読み、型ヒントに関するGuidoによるこのプレゼンテーションを見ることをお勧めします。

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

Pythonの動的な性質により、使用されているオブジェクトの型を推測または確認することは特に困難です。この事実は、開発者が自分が書いていないコードで何が起こっているのかを正確に理解することを困難にします。最も重要なのは、多くの IDE ( PyCharmPyDevが思い浮かびます) に見られる型チェック ツールが制限されていることです。オブジェクトのタイプを示す指標はありません。その結果、彼らは (プレゼンテーションで述べたように) 約 50% の成功率で型を推測しようとすることに頼っています。


型ヒントのプレゼンテーションから 2 つの重要なスライドを引用するには、次のようにします。

ヒントを入力する理由

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

静的型チェッカーを使用する理由

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

この小さな紹介の結びのメモとして: これはオプションの機能であり、私が理解していることから、静的型付けの利点の一部を享受するために導入されました。

通常、これについて心配する必要はなく、使用する必要もありませ(特に、Python を補助スクリプト言語として使用する場合)。必要な堅牢性、制御、および追加のデバッグ機能を提供するため、大規模なプロジェクトを開発するときに役立ちます。


mypy を使用したタイプヒント:

この回答をより完全にするために、少しデモンストレーションが適していると思います。mypyPEP で提示されている Type Hints に影響を与えたライブラリであるを使用します。これは主に、この質問にぶつかり、どこから始めればよいか迷っている人のために書かれています。

その前に、次のことを繰り返します: PEP 484は何も強制しません。関数注釈の方向性を設定し、型チェックをどのように実行できるか/実行する必要があるかについてのガイドラインを提案するだけです。関数に注釈を付けて、好きなだけヒントを与えることができます。Python 自体はアノテーションを使用しないため、アノテーションの有無に関係なく、スクリプトは引き続き実行されます。

とにかく、PEP に記載されているように、ヒントの型は一般に 3 つの形式を取る必要があります。

  • 関数注釈 ( PEP 3107 )。
  • ビルトイン/ユーザー モジュールのスタブ ファイル。
  • 最初の 2 つの形式を補足する特別 # type: typeなコメント。(参照:コメントの Python 3.6 更新については、変数注釈とは何ですか?# type: type )

typingさらに、 で導入された新しいモジュールと組み合わせて型ヒントを使用する必要がありますPy3.5。その中で、多くの (追加の) ABC (抽象基本クラス) が、静的チェックで使用するヘルパー関数とデコレーターと共に定義されています。のほとんどの ABCcollections.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)、 list の内容を出力するだけ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のみを追加するか、 のコンテンツのタイプを変更して、任意の値が受け入れられることを示すことによって修正できます(が からインポートされた後に直感的に実行されます)。straList[Any]Anytyping

関数注釈は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モジュール、PEP、またはドキュメントを見る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 モジュールとして表示されます。

次のリストに追加のチェッカー/関連パッケージを見つけた場合 (または提案された場合) に追加しようとします。

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

  • Mypy : ここで説明されているとおり。
  • PyType : Google では、私が収集したものとは異なる表記法を使用しています。おそらく一見の価値があります。

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

  • typeshed:標準ライブラリのさまざまなスタブ ファイルを格納する公式の Python リポジトリ。

実際、このtypeshedプロジェクトは、自分のプロジェクトで型ヒントがどのように使用されるかを確認するのに最適な場所の 1 つです。例として、対応するファイル内のクラス__init__ダンダーを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クラスについては、イニシャライザで引数をとらないか、任意の型から an へのシングルを取得するMapping、任意の型の an を取得できることがわかります。int Iterable


注意: 1 つ忘れていたのは、typingモジュールが暫定的に導入されたことです。PEP 411から:

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

したがって、ここでは塩を少し加えてください。重要な方法で削除または変更されるとは思えませんが、それを知ることはできません。


**完全に別のトピックですが、型ヒントの範囲内で有効です: PEP 526: 変数注釈の構文# typeは、ユーザーが単純なvarname: typeステートメントで変数の型に注釈を付けることを可能にする新しい構文を導入することで、コメントを置き換える試みです。

変数注釈とはを参照してください。、前述のように、これらについて簡単に紹介します。

于 2015-09-14T06:49:50.233 に答える
68

ジムの精巧な答えに追加:

typingモジュールを確認してください。このモジュールは、 PEP 484で指定されている型ヒントをサポートしています。

たとえば、次の関数は型の値を受け取って返しstr、次のように注釈が付けられています。

def greeting(name: str) -> str:
    return 'Hello ' + name

このtypingモジュールは以下もサポートしています。

  1. エイリアシングを入力します。
  2. コールバック関数の型ヒント。
  3. ジェネリックス- 抽象基本クラスが拡張され、サブスクリプションをサポートして、コンテナー要素の予期される型を示します。
  4. ユーザー定義ジェネリック型- ユーザー定義クラスは、ジェネリック クラスとして定義できます。
  5. 任意のタイプ- すべてのタイプは Any のサブタイプです。
于 2016-06-27T03:46:37.203 に答える