93 votes

Dépendance circulaire en Python

J'ai deux fichiers, node.py y path.py qui définissent deux classes, Node y Path respectivement.

Jusqu'à aujourd'hui, la définition de Path a fait référence à la Node et c'est pourquoi j'avais fait

from node.py import *

dans le path.py archivo.

Cependant, j'ai créé aujourd'hui une nouvelle méthode pour les Node qui fait référence à la Path objet.

J'ai eu des problèmes en essayant d'importer path.py : J'ai essayé, et lorsque le programme s'est exécuté et a appelé la fonction Path qui utilise la méthode Node une exception s'est levée à propos de Node n'est pas définie.

Que dois-je faire ?

2 votes

3 votes

Essayez-vous d'avoir une classe par fichier ? C'est la raison pour laquelle cela fonctionne rarement bien.

4 votes

D'accord avec S.Lott. Python n'est pas Java. Vous n'avez pas besoin d'une classe par fichier.

131voto

Nadia Alramli Points 40381

Importer des modules Python est un excellent article qui explique les importations circulaires en Python.

La façon la plus simple de résoudre ce problème est de déplacer l'importation du chemin d'accès à la fin du module de nœuds.

1 votes

D'accord, mais le problème, c'est que j'ai deux autres modules tree.py y block.py dans ce paquet qui requièrent node.py et sont exigées par path.py . Je suis donc censé les mettre tous dans un seul fichier ? J'aimais bien avoir un module par classe.

3 votes

Avez-vous essayé ma suggestion ? Elle fonctionnera probablement. Il suffit de déplacer l'importation à la fin du fichier. Je vous conseille de lire l'article pour comprendre pourquoi cela se produit.

12 votes

D'ailleurs, y a-t-il une raison autre que le fait que vous "aimiez" le programme pour que vous souhaitiez une classe par module ? Les quelques fois où j'ai vu cette préférence, c'était parce qu'elle était similaire à Java.

32voto

mircealungu Points 716

Une autre approche consiste à importer l'un des deux modules suivants uniquement dans la fonction là où vous en avez besoin dans l'autre. Bien entendu, cette méthode est la plus efficace si vous n'avez besoin de l'information que dans une seule fonction ou dans un petit nombre de fonctions :

# in node.py 
from path import Path
class Node 
    ...

# in path.py
class Path
  def method_needs_node(): 
    from node import Node
    n = Node()
    ...

6voto

Bryan Roach Points 28

Il se peut que vous n'ayez pas besoin d'importer Path en node.py pour que Path y Node de se servir les uns des autres.

# in __init__.py  (The order of imports should not matter.)
from .node import Node
from .path import Path

# in path.py 
from . import Node
class Path
  ...

  def return_something_pathy(self): 
    ...

# in node.py
class Node
  def __init__(self, path): 
    self.path = path
    ...

  def a_node_method():
    print(self.path.return_something_pathy())

Pour qu'il soit clair que Node fait appel à Path ajouter une indication de type. Il existe une fonctionnalité disponible à partir de Python 3.7 pour prendre en charge les références directes dans les annotations de type, décrite dans PEP 563 .

# in node.py  (Now with type hinting.)
from __future__ import annotations

class Node
  def __init__(self, path: Path): 
    self.path = path
    ...

  def a_node_method():
    print(self.path.return_something_pathy())

Je suis tombé sur une autre solution pour vous sortir d'un trou d'importation circulaire en Python. est un excellent article de blog qui m'a appris cela.

5voto

Iain Hunter Points 663

Je préfère briser une dépendance circulaire en déclarant l'une des dépendances dans le constructeur de l'autre classe dépendante. À mon avis, cela permet de conserver un code plus propre et d'accéder facilement à toutes les méthodes qui requièrent la dépendance.

Dans mon cas, j'ai donc un CustomerService et un UserService qui dépendent l'un de l'autre. Je brise la dépendance circulaire comme suit :

class UserService:

    def __init__(self):
        # Declared in constructor to avoid circular dependency
        from server.portal.services.admin.customer_service import CustomerService
        self.customer_service = CustomerService()

    def create_user(self, customer_id: int) -> User:
        # Now easy to access the dependency from any method
        customer = self.customer_service.get_by_id(customer_id)

1voto

Richard Vodden Points 116

Une autre méthode consiste à les définir dans le même module et à retarder la définition des types. C'est un peu comme ça :

class Node:
   _path_type: type = None

   def method_needs_path(self):
       p = self._path_type()
       ...

class Path:
    def method_needs_node(self):
       n = Node()

Node._path_type = Path

Il serait peut-être plus agréable d'être symétrique à cet égard :

class Node:
   _path_type: type = None

   def method_needs_path(self):
       p = self._path_type()
       ...

class Path:
    _node_type: type = None

    def method_needs_node(self):
       n = Node()

Node._path_type = Path
Path._node_type = Node

Cela pourrait également se faire en plusieurs modules :

# in node.py
class Node:
   _path_type: type = None

   def method_needs_path(self):
       p = self._path_type()
       ...

# in path.py
from .node import Node

class Path:
    _node_type: type = None

    def method_needs_node(self):
       n = self._node_type()

Node._path_type = Path
Path._node_type = Node

# in __init__.py (note that order is important now)
from .node import Node
from .path import Path

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X