简体   繁体   English

Python 中的循环依赖

[英]Circular dependency in Python

I have two files, node.py and path.py , which define two classes, Node and Path , respectively.我有两个文件, node.pypath.py ,它们分别定义了两个类, NodePath

Up to today, the definition for Path referenced the Node object, and therefore I had done直到今天, Path的定义引用了Node对象,因此我已经完成了

from node.py import *

in the path.py file.path.py文件中。

However, as of today I created a new method for Node that references the Path object.但是,截至今天,我为Node创建了一个引用Path对象的新方法。

I had problems when trying to import path.py : I tried it, and when the program ran and called the Path method that uses Node , an exception rose about Node not being defined.我在尝试导入path.py时遇到了问题:我试过了,当程序运行并调用使用NodePath方法时,出现了一个关于Node未定义的异常。

What do I do?我该怎么办?

Importing Python Modules is a great article that explains circular imports in Python. Importing Python Modules是一篇很好的文章,它解释了 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.所以在我的情况下,我有一个 CustomerService 和一个 UserService,它们相互依赖。 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.您可能不需要在node.py中导入Path以便PathNode相互使用。

# 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.为了明确Node正在使用Path ,添加类型提示。 There is a feature available starting with Python 3.7 to support forward references in type annotations, described in PEP 563 .从 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())

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. 我遇到了另一个解决方案,可以将您从 Python 中的圆形导入漏洞中挖掘出来,这是一篇很棒的博客文章,它教会了我这一点。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM