Круговая зависимость в Python
У меня есть два файла, node.py
а также path.py
, которые определяют два класса, Node
а также Path
соответственно.
До сегодняшнего дня определение Path
ссылался на Node
объект, и поэтому я сделал
from node.py import *
в path.py
файл.
Однако на сегодняшний день я создал новый метод для Node
что ссылается на Path
объект.
У меня были проблемы при попытке импортировать path.py
: Я попробовал, и когда программа запустилась и вызвал Path
метод, который использует Node
Возникло исключение Node
не определяется
Что я делаю?
5 ответов
Импорт модулей Python - это отличная статья, в которой объясняется циклический импорт в Python.
Самый простой способ исправить это - перенести путь импорта в конец модуля узла.
Еще один подход - импортировать один из двух модулей только в той функции, в которой он вам нужен, в другом. Конечно, это работает лучше всего, если вам это нужно только в одной или нескольких функциях:
# in node.py
from path import Path
class Node
...
# in path.py
class Path
def method_needs_node():
from node import Node
n = Node()
...
Возможно, вам не потребуется импортировать Path
в node.py
Для того чтобы Path
а также Node
использовать друг друга.
# 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())
Чтобы было ясно, что Node
использует Path
, добавьте подсказку типа. Начиная с Python 3.7 доступна функция поддержки прямых ссылок в аннотациях типов, описанная в 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())
Я наткнулся на еще одно решение, чтобы выбраться из круговой дыры для импорта в Python - отличный пост в блоге, который научил меня этому.
Я предпочитаю разорвать циклическую зависимость, объявив одну из зависимостей в конструкторе другого зависимого класса. На мой взгляд, это делает код более аккуратным и обеспечивает легкий доступ ко всем методам, которым требуется зависимость.
Так что в моем случае у меня есть CustomerService и UserService, которые зависят друг от друга. Я нарушаю круговую зависимость следующим образом:
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)
Другой способ - определить их оба в одном модуле и отложить определение типов. Примерно так:
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
Может быть, лучше проявить симметричность по этому поводу:
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
Это также можно сделать в нескольких модулях:
# 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