web-dev-qa-db-fra.com

Astuces de type: résoudre la dépendance circulaire

Ce qui suit produit NameError: name 'Client' is not defined. Comment puis-je le résoudre?

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


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

Vous pouvez utiliser un référence avant en utilisant un chaîne nom pour la classe Client non encore définie :

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

À partir de Python 3.7 , vous pouvez également reporter tous l'analyse des annotations au moment de l'exécution en ajoutant l'importation __future__ suivante en haut de votre module:

from __future__ import annotations

à quel point les annotations sont stockées sous forme de représentations de chaîne de l'arbre de syntaxe abstraite pour l'expression; vous pouvez utiliser typing.get_type_hints() pour résoudre ces problèmes (et résoudre les références directes comme utilisé ci-dessus).

Voir PEP 563 - Évaluation différée des annotations pour plus de détails; ce comportement sera la valeur par défaut dans Python 4.0.

67
Martijn Pieters

Si vous êtes sur Python 3.7+, utilisez from __future__ import annotationscomme mentionné dans une autre réponse. Cependant, si vous ne pouvez pas encore utiliser 3.7 en raison d'une limitation du système d'exploitation (comme Cygwin à partir du 2019-06-03), vous pouvez utiliser le module typing pour satisfaire ces types de problèmes de dépendance directe/circulaire.

Pardonnez l'exemple artificiel, mais cela devrait illustrer l'utilité de cette méthodologie.

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
Timothy C. Quinn