web-dev-qa-db-ja.com

Python 3.5の型ヒットとは

Python 3.5で話題になっている機能の1つはtype hintsと言われています。

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

190
Vaulstein

PEP 483PEP 484 を読んで、 this TypeでのGuidoのプレゼンテーションを見ることをお勧めします。ヒント。

一言で言えばタイプヒントは文字通り言葉の意味であり、使用しているオブジェクトのタイプを示唆します

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


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

ヒントを入力する理由

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

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

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

この小さな紹介の最後のメモとして:これはoptional機能ですそして、私が理解していることから、静的型付けのいくつかの利点を享受するために導入されました。

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


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

この回答をより完全なものにするために、ちょっとしたデモンストレーションが適していると思います。 mypy を使用します。これは、PEPに表示されるTypeヒントを刺激したライブラリです。これは主に、この質問にぶつかり、どこから始めるべきか疑問に思う人のために書かれています。

その前に、次のことを繰り返してみましょう。 PEP 484 は何も強制しません。関数の注釈の方向を設定し、how型チェックを実行できるかどうかのガイドラインを提案するだけです。関数に注釈を付けて、好きなだけヒントを付けることができます。 Python自体はそれらを使用しないため、注釈の有無にかかわらずスクリプトは引き続き実行されます。

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

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

さらに、Py3.5で導入された新しい typing モジュールと組み合わせて、タイプヒントを使用することもできます。その中で、多くの(追加の)ABC(抽象基本クラス)が、静的チェックで使用するヘルパー関数とデコレーターと共に定義されています。 collections.abcのほとんどのABCsは含まれていますが、サブスクリプションを許可するためにGeneric形式になっています(__getitem__()メソッドを定義することにより)。

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

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

まず、特別なコメントを使用した場合に得られるいくつかの動作を観察するのは興味深いことです。特別な# type: typeコメントを変数の割り当て中に追加して、オブジェクトを直接推測できない場合にオブジェクトのタイプを示すことができます。通常、単純な割り当ては簡単に推測できますが、リストのような他のもの(内容に関して)は推測できません。

注:Containersの派生物を使用し、そのコンテナの内容を指定する必要がある場合は、musttypingモジュールのジェネリックタイプを使用します。 これらはインデックス作成をサポートしています。

# 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のコンテンツのタイプを変更して、値が受け入れられることを示すことで修正できます(Anytypingからインポートされた後、List[Any]で直感的に実行されます)。

関数アノテーションは、関数シグネチャの各パラメーターの後に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)は、作成/使用するモジュールの注釈付きインターフェイスです。これらには、型チェックする関数のシグネチャが含まれ、関数の本体は破棄されます。 randfunc.pyという名前のモジュールに3つのランダム関数のセットが与えられた場合、これを感じるために:

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のType Hintsの基本概念を理解できるはずです。使用されている型チェッカーはmypyでしたが、徐々に多くのポップアップが表示されるようになるはずです。一部はIDEの内部( PyCharm 、)およびその他の標準pythonモジュールとして。それらが見つかった場合(または提案された場合)は、次のリストにチェッカー/関連パッケージを追加してみます。

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

  • Mypy :ここで説明します。
  • PyType :Googleでは、私が収集したものとは異なる表記を使用しています。

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

  • typeshed: Official Python標準ライブラリ用の各種スタブファイルを格納するリポジトリ。

typeshedプロジェクトは、実際に、独自のプロジェクトで型ヒントがどのように使用されるかを確認するのに最適な場所の1つです。例として、 対応する__init__ファイル内のCounter class.pyi dundersを見てみましょう。

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を取得できることがわかります。


Notice:私が言及するのを忘れていた1つのことは、typingモジュールが暫定ベースで導入されたことです。 FromPEP 411

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

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


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

を参照してくださいPython 3.6の変数注釈とは?、前述のように、これらの小さなイントロについて。

272

ジムの巧妙な答えに加えて:

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

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

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

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

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

新しくリリースされたPyCharm 5はタイプヒントをサポートします。それについての彼らのブログ記事で( PyCharm 5の中のPython 3.5型ヒント - を見てください )彼らはどんな型ヒントがあるかどうかについての素晴らしい説明を使い方のいくつかの例と実例と共に提供しますあなたのコードでそれらを。

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

PyCharmはPython 2.7用のPyPI、Python 3.2-3.4からの型付けモジュールをサポートします。 2.7では、関数アノテーションがPython 3.0で追加されたので、*。pyiスタブファイルに型のヒントを入れる必要があります。

22
tsvenson