web-dev-qa-db-ja.com

タイプヒント:循環依存関係を解決する

以下はNameError: name 'Client' is not definedを生成します。どうすれば解決できますか?

class Server():
    def register_client(self, client: Client)
        pass


class Client():
    def __init__(self, server: Server):
        server.register_client(self)
47
Tamriel

未定義のClientクラスにstringの名前を使用することで、 前方参照 を使用できます:

_class Server():
    def register_client(self, client: 'Client')
        pass
_

Python 3.7 以降、allランタイム解析を延期することもできます。モジュールの上部にある次の___future___インポート:

_from __future__ import annotations
_

その時点で、注釈は式の抽象構文ツリーの文字列表現として格納されます。 typing.get_type_hints() を使用してそれらを解決できます(上記で使用した前方参照を解決できます)。

詳細は PEP 563-アノテーションの評価の延期 を参照してください。この動作は、Python 4.0ではデフォルトです。

67
Martijn Pieters

Python 3.7+を使用している場合は、別の回答でfrom __future__ import annotations言及されているように を使用してください。ただし、OSの制限のために3.7をまだ使用できない場合( Cygwin(2019年6月3日現在)では、 typing モジュールを使用して、これらのタイプの前方/循環依存の問題を満たすことができます。

不自然な例はご容赦ください。これは、この方法論の有用性を示しているはずです。

from typing import TypeVar, Generic

T_Server = TypeVar('T_Server')
T_Client = TypeVar('T_Client')


class Server(Generic[T_Server]):
    clients: list = None

    def __init__(self):
        self.clients=[]

    def register_client(self, client: T_Client) -> None:
        self.clients.append(client)
        print('Client `%s` registered with server' % client.name)

    def print_clients(self) -> None:
        for i, client in enumerate(self.clients):
            print('client %i: %s' % (i, client.name))

    @staticmethod
    def build_clone(server: T_Server) -> T_Server:
        svr_new: Server = Server()
        for client in server.clients:
            svr_new.register_client(client)
        return svr_new

class Client():
    name: str = None
    def __init__(self, name: str, server: T_Server):
        self.name = name
        server.register_client(self)


svr = Server()
cli = Client('foo', svr)
svr.print_clients()

svr_clone = Server.build_clone(svr)
svr_clone.print_clients()
2