囲んでいるクラスの型を使用してメソッドに型ヒントを設定するにはどうすればいいですか? 質問する

囲んでいるクラスの型を使用してメソッドに型ヒントを設定するにはどうすればいいですか? 質問する

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

参考文献:

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

おそらく面倒すぎるでしょう。

おすすめ記事