TL; DR:Python 4.0を使用している場合は動作します。本日(2019年)以降、3.7以降では、この機能をfutureステートメント(from __future__ import annotations
)を使用してオンにする必要があります。Python3.6 以下では、文字列を使用します。
あなたはこの例外を得たと思います:
NameError: name 'Position' is not defined
これは、Position
Python 4を使用していない限り、アノテーションで使用する前に定義する必要があるためです。
Python 3.7以降: from __future__ import annotations
Python 3.7はPEP 563を導入しました:アノテーションの評価の延期。futureステートメントを使用するモジュールは、from __future__ import annotations
注釈を文字列として自動的に保存します。
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
これはPython 4.0のデフォルトになる予定です。Pythonは依然として動的に型付けされた言語であるため、実行時に型チェックが行われないため、注釈の入力によるパフォーマンスへの影響はないはずですよね?違う!python 3.7にするために使用タイピングモジュールの前にコアで最も遅いpythonモジュールの1ので、もしあればimport typing
、あなたが表示されます時間は、パフォーマンスの増加7まであなたが3.7にアップグレードする場合。
Python <3.7:文字列を使用する
PEP 484によると、クラス自体ではなく文字列を使用する必要があります:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Djangoフレームワークを使用する場合、Djangoモデルも前方参照に文字列を使用するため、これはよく知られている可能性があります(外部モデルがself
まだ宣言されているか、まだ宣言されていない外部キー定義)。これはPycharmやその他のツールで動作するはずです。
出典
旅行を節約するためのPEP 484およびPEP 563の関連部分:
前方参照
タイプヒントにまだ定義されていない名前が含まれている場合、その定義は文字列リテラルとして表現され、後で解決される場合があります。
これが一般的に発生する状況は、コンテナクラスの定義であり、定義されているクラスは一部のメソッドのシグネチャで発生します。たとえば、次のコード(単純なバイナリツリー実装の開始)は機能しません。
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
これに対処するには、次のように記述します。
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
文字列リテラルには有効なPython式が含まれている必要があります(つまり、compile(lit、 ''、 'eval')は有効なコードオブジェクトである必要があります)。モジュールが完全に読み込まれると、エラーなしで評価されます。評価されるローカルおよびグローバル名前空間は、同じ関数へのデフォルトの引数が評価されるのと同じ名前空間である必要があります。
およびPEP 563:
Python 4.0では、関数と変数のアノテーションは定義時に評価されなくなりました。代わりに、文字列形式がそれぞれの__annotations__
辞書に保存されます。静的型チェッカーは動作に違いはありませんが、実行時にアノテーションを使用するツールは延期された評価を実行する必要があります。
...
上記の機能は、次の特別なインポートを使用してPython 3.7以降で有効にできます。
from __future__ import annotations
代わりにやりたくなるかもしれないこと
A.ダミーを定義する Position
クラス定義の前に、ダミー定義を配置します。
class Position(object):
pass
class Position(object):
...
これはを取り除き、NameError
問題なく見えるかもしれません:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
しかし、そうですか?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B.注釈を追加するためのモンキーパッチ:
アノテーションを追加するために、Pythonメタプログラミングの魔法を試して、クラス定義をモンキーパッチするデコレータを作成することができます。
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
デコレータはこれと同等のものを担当する必要があります:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
少なくともそれは正しいようです:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
おそらくあまりにも多くのトラブル。
結論
3.6 from __future__ import annotations
以前を使用している場合は、クラス名を含む文字列リテラルを使用してください。3.7では、それが機能します。