Python 中的循环依赖

我有两个文件 node.pypath.py,它们分别定义了两个类 NodePath

到今天为止,Path的定义引用了 Node对象,因此我已经做了

from node.py import *

path.py文件中。

但是,从今天开始,我为 Node创建了一个引用 Path对象的新方法。

我在尝试导入 path.py时遇到了问题: 我尝试了,当程序运行并调用使用 NodePath方法时,出现了一个未定义 Node的异常。

我该怎么办?

99473 次浏览

Importing Python Modules is a great article that explains circular imports in Python.

The easiest way to fix this is to move the path import to the end of the node module.

One other approach is importing one of the two modules only in the function where you need it in the other. Sure, this works best if you only need it in one or a small number of functions:

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


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

I prefer to break a circular dependency by declaring one of the dependencies in the constructor of the other dependent class. In my view this keeps the code neater, and gives easy access to all methods who require the dependency.

So in my case I have a CustomerService and a UserService who depend on each other. I break the circular dependency as follows:

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)

You may not need to import Path in node.py in order for Path and Node to make use of one another.

# 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())

To make it clear that Node is making use of Path, add type hinting. There is a feature available starting with Python 3.7 to support forward references in type annotations, described in 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())

I came across a Yet another solution to dig you out of a circular import hole in Python is a great blog post which taught me this.

Another method is to define them both in the same module, and to delay defining the types. A little like this:

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


It may be nicer to be symmetrical about this:

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


This could also be done in multiple 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