Python 3 には次のコードがあります:
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: Position) -> Position:
return Position(self.x + other.x, self.y + other.y)
しかし、私のエディター (PyCharm) では、参照をPosition
解決できない (__add__
メソッド内) と表示されます。戻り値の型が 型であると予想されるように指定するにはどうすればよいでしょうかPosition
?
これは実際には PyCharm の問題だと思います。実際には警告とコード補完の情報を使用しています。
ただし、間違っていて、別の構文を使用する必要がある場合は訂正してください。
ベストアンサー1
TL;DR : 今日 (2019) 時点で、Python 3.7 以降では、「future」ステートメントを使用してこの機能をオンにすることができますfrom __future__ import annotations
。
(によって有効化された動作は、Pythonの将来のバージョンではデフォルトになるfrom __future__ import annotations
可能性があり、つもりだったPython 3.10でデフォルトになる予定です。ただし、3.10での変更元に戻されました最後の瞬間に、そして今は全く起こらないかもしれない。Python 3.11含まれない)
Python 3.6 以下では、文字列を使用する必要があります。
次のような例外が発生したようです:
NameError: name 'Position' is not defined
これはPosition
、Pythonをペップ563変更が有効になりました。
Python 3.11+:from typing import Self
from typing import Self
class Position:
def __add__(self, other: Self) -> Self:
...
Python バージョン 3.11 未満の場合、以下を使用できます。
from typing_extensions import Self
参考文献:
- https://docs.python.org/3/whatsnew/3.11.html#pep-673-自己型
- https://docs.python.org/3/library/typing.html#typing.Self
- python-type-self は、Python の基本的なプログラミング言語です。
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 3.10でデフォルトになる予定でしたが、この変更は延期されました。Pythonは依然として動的型付け言語であり、実行時に型チェックは行われないため、型付けアノテーションはパフォーマンスに影響を与えないはずです。そうではありません!Python 3.7より前では、typingモジュールはコアの中で最も遅いPythonモジュールの1つモジュールのインポートを伴うコードでは、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 3.10 では、関数と変数の注釈は定義時に評価されなくなります。代わりに、それぞれの
__annotations__
辞書に文字列形式が保存されます。静的型チェッカーでは動作に違いはありませんが、実行時に注釈を使用するツールでは評価を延期する必要があります。...
Python 3.7 で将来の動作を有効にする
上記の機能は、次の特別なインポートを使用して 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
おそらく面倒すぎるでしょう。