Python 3.5の型ヒントとは何ですか?


250

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

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


4
あなたは見てとるべきPEP 484からリンクされている公式のchangelogを
Stefan

1
@AvinashRaj:リリースに関する良い議論がここで
Vaulstein

1
C-APIの使用例がこのPEP 484で完全に無視されるのは残念です。特にCythonとNumbaのタイプヒントは無視されます。
denfromufa

回答:


343

PEP 483PEP 484を読んで、タイプヒンティングに関するGuidoによるこのプレゼンテーションを見ることをお勧めします。

一言で言えばタイプヒンティングは、文字通り言葉が何を意味するか、あなたが使用しているオブジェクト(複数可)のタイプをヒントです

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


タイプヒントプレゼンテーションから2つの重要なスライドを取得するには:

なぜタイプヒント?

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

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

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

この小さな紹介の締めくくりとして、これはオプションの機能であり、私が理解しているところから、静的型付けのいくつかの利点を享受するために紹介されました。

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


mypyでHintingと入力します

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

私は前にそれは私が次のことをあらためて表明しましょう:PEP 484には何も強制しません。それは単に関数注釈の方向を設定し、型チェックをどのように実行できる/すべきかについてのガイドラインを提案することです。関数に注釈を付けたり、好きなだけヒントを与えたりすることができます。Python自体は注釈を使用しないため、注釈の存在に関係なくスクリプトは実行されます。

とにかく、PEPに記載されているように、ヒントタイプは一般に次の3つの形式を取ります。

  • 関数の注釈。(PEP 3107
  • 組み込み/ユーザーモジュール用のスタブファイル。
  • # type: type最初の2つの形式を補足する特別なコメント。(コメント:Python 3.6アップデートのコメントについては、Python 3.6の変数アノテーションとは何ですか?# type: type

さらに、でtyping導入された新しいモジュールと共に型ヒントを使用する必要がありますPy3.5。その中で、多くの(追加の)ABC(Abstract Base Classes)が、静的チェックで使用するヘルパー関数とデコレーターとともに定義されています。ほとんどが含まABCscollections.abcていますが、Generic__getitem__()メソッドを定義することにより)サブスクリプションを許可するためのフォームに含まれています。

これらのより詳細な説明に興味がある人のために、これmypy documentationは非常にうまく書かれており、チェッカーの機能を実証/説明する多くのコードサンプルがあります。それは間違いなく読む価値があります。

関数の注釈と特別なコメント:

まず、特別なコメントを使用したときに発生する可能性のある動作を観察するのは興味深いことです。# type: type変数を割り当てるときに特別なコメントを追加して、オブジェクトを直接推論できない場合にオブジェクトのタイプを示すことができます。単純な割り当ては一般に簡単に推測できますが、リストなど(内容に関して)は割り当てられません。

注:の派生物を使用しContainers、そのコンテナーのコンテンツを指定する必要がある場合は、モジュールのジェネリック型を使用する必要があり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]後、で直感的に実行)。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'>}

私たちが完全な初心者である場合、またはPy2.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このプロジェクトは、実際にあなたがタイプヒンティングは、あなた自身のプロジェクトで使用されるかもしれない方法を確認するために見ることができる最高の場所の一つです。例として、対応するファイル内のクラス__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クラス我々はそれがどちらか、その初期化子で引数を取りませんシングルを得ることができることを見ることができますMappingに任意の型からint または取るIterableあらゆる種類の。


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

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

塩を少し入れて、ここに物を置いてください。私はそれがかなりの方法で削除または変更されるかどうか疑わしいですが、誰も知ることができません。


**型のヒントの範囲内の別の完全な話題が、有効な:PEP 526:構文変数注釈については、置き換えるための努力で# type、ユーザーが簡単なの変数のタイプに注釈を付けることができます新しい構文導入して、コメントをvarname: type発言を。

参照のPython 3.6での変数の注釈は何?、前述のように、これらの小さなイントロのため。


3
「Pythonの非常に動的な性質のため、使用されているオブジェクトのタイプを推測またはチェックすることは特に困難です。」あなたは静的チェックを参照していますよね?
bsam 2017

53

ジムの手の込んだ答えに追加:

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

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

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

typingモジュールもサポートしています。

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

26

新しくリリースされたPyCharm 5は、タイプヒントをサポートしています。それに関する彼らのブログ投稿(PyCharm 5のPython 3.5型ヒントを参照)で、型ヒントとは何かについての優れた説明を提供しています、あなたのコード内でそれらを使用する方法についていくつかの例やイラストと一緒に。

さらに、このコメントで説明されているように、Python 2.7でもサポートされています

PyCharmは、PyPI for Python 2.7、Python 3.2-3.4の型付けモジュールをサポートしています。2.7では、関数の注釈がPython 3.0で追加されたため、*。pyiスタブファイルに型ヒントを配置する必要があります


0

タイプヒントは動的言語に最近追加されたもので、何十年もの間、ハンガリー語のように単純な命名規則を採用していました(最初の文字がb =ブーリアン、c =文字、d =辞書、i =整数、l =リスト、n =数値のオブジェクトラベル) 、s =文字列、t =タプル)は不要であり、面倒でしたが、今や決定しました、待ってください...言語(type())を使用してオブジェクトを認識するのは非常に面倒で、私たちの豪華なIDE複雑な処理を行う必要があり、動的に割り当てられたオブジェクトの値はとにかくまったく役に立たないのに対し、単純な命名規則では、開発者は一目ですべてを解決できた可能性があります。


率直に言って、これは答えというよりは怒りのように聞こえます。
Dimitris Fasarakis Hilliard

-1

タイプヒントは保守性のためのものであり、Pythonによって解釈されません。以下のコードでは、その行def add(self, ic:int)は次のreturn...行までエラーになりません。

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic
    
c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.