Python 3.5 で最も話題になっている機能の 1 つは、型ヒントです。
タイプ ヒントの例は、この記事とこの記事で言及されていますが、責任を持ってタイプ ヒントを使用することについても言及されています。誰かがそれらについて、またいつ使用すべきか、いつ使用すべきでないかを説明できますか?
PEP 483とPEP 484を読み、型ヒントに関するGuidoによるこのプレゼンテーションを見ることをお勧めします。
一言で言えば:タイプヒンティングは文字通り言葉が意味するものです。使用しているオブジェクトのタイプをヒントします。
Pythonの動的な性質により、使用されているオブジェクトの型を推測または確認することは特に困難です。この事実は、開発者が自分が書いていないコードで何が起こっているのかを正確に理解することを困難にします。最も重要なのは、多くの IDE ( PyCharmとPyDevが思い浮かびます) に見られる型チェック ツールが制限されていることです。オブジェクトのタイプを示す指標はありません。その結果、彼らは (プレゼンテーションで述べたように) 約 50% の成功率で型を推測しようとすることに頼っています。
型ヒントのプレゼンテーションから 2 つの重要なスライドを引用するには、次のようにします。
TypeErrors
。.
があり、オブジェクトに対して定義されていないメソッド/属性がポップアップ表示されます。この小さな紹介の結びのメモとして: これはオプションの機能であり、私が理解していることから、静的型付けの利点の一部を享受するために導入されました。
通常、これについて心配する必要はなく、使用する必要もありません(特に、Python を補助スクリプト言語として使用する場合)。必要な堅牢性、制御、および追加のデバッグ機能を提供するため、大規模なプロジェクトを開発するときに役立ちます。
この回答をより完全にするために、少しデモンストレーションが適していると思います。mypy
PEP で提示されている Type Hints に影響を与えたライブラリであるを使用します。これは主に、この質問にぶつかり、どこから始めればよいか迷っている人のために書かれています。
その前に、次のことを繰り返します: PEP 484は何も強制しません。関数注釈の方向性を設定し、型チェックをどのように実行できるか/実行する必要があるかについてのガイドラインを提案するだけです。関数に注釈を付けて、好きなだけヒントを与えることができます。Python 自体はアノテーションを使用しないため、アノテーションの有無に関係なく、スクリプトは引き続き実行されます。
とにかく、PEP に記載されているように、ヒントの型は一般に 3 つの形式を取る必要があります。
# 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
のみを追加するか、 のコンテンツのタイプを変更して、任意の値が受け入れられることを示すことによって修正できます(が からインポートされた後に直感的に実行されます)。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
モジュール、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 モジュールとして表示されます。
次のリストに追加のチェッカー/関連パッケージを見つけた場合 (または提案された場合) に追加しようとします。
私が知っているチェッカー:
関連パッケージ/プロジェクト:
実際、この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
ステートメントで変数の型に注釈を付けることを可能にする新しい構文を導入することで、コメントを置き換える試みです。
変数注釈とはを参照してください。、前述のように、これらについて簡単に紹介します。
ジムの精巧な答えに追加:
typing
モジュールを確認してください。このモジュールは、 PEP 484で指定されている型ヒントをサポートしています。
たとえば、次の関数は型の値を受け取って返しstr
、次のように注釈が付けられています。
def greeting(name: str) -> str:
return 'Hello ' + name
このtyping
モジュールは以下もサポートしています。