尽管绝对进口,但循环进口

Circular import despite absolute imports

我在 Python 3.9 中遇到循环导入问题。 我知道有很多关于这个主题的文章、问题和答案,我确实阅读了很多关于如何规避的文章,但我无法弄清楚为什么它会发生在我的案例中。据我所知,可以通过绝对导入来规避循环导入:

这是我的堆栈跟踪:

partially initialized module 'myproject.myapp.service.linepattern.corner' has no attribute 'Corner' (most likely due to a circular import)

  File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/line.py", line 8, in Line
    def __init__(self, cornerRight: cc.Corner, cornerLeft: cc.Corner) -> None:
  
  File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/line.py", line 7, in <module>
    class Line(ABC):
  
  File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/corner.py", line 2, in <module>
    import myproject.myapp.service.linepattern.line as cl
  
  File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/cornermanager.py", line 2, in <module>
    import myproject.myapp.service.linepattern.corner as cc
  
  File "/mnt/d/User/Programming/MyProject/myproject/tests/myapp/test_cornermanager.py", line 3, in <module>
    import myproject.myapp.service.linepattern.cornermanager as cm

这是我的文件层次结构

/mnt/d/User/Programming/MyProject
├── manage.py
├── myproject
│   ├── myapp
│   │   ├── __init__.py
│   │   ├── service
│   │   │   └── linepattern
│   │   │       ├── cornermanager.py
│   │   │       ├── corner.py
│   │   │       ├── line.py
│   │   │       ├── __init__.py
│   ├── settings.py
│   ├── tests
│   │   ├── myapp
│   │   │   ├── __init__.py
│   │   │   └── test_cornermanager.py

最后是我的代码

cornermanager.py

import myproject.myapp.service.linepattern.corner as cc

class CornerManager:
    def __init__(self) -> None:
        self.cornerList: List[cc.Corner]
        for i in range(1, 2):
            self.cornerList.append(cc.Corner(i, self))
        for corner in self.cornerList:
            corner.initializeLines()

    def corner(self, cornerNr: int) -> cc.Corner:
        return self.cornerList[cornerNr - 1]

corner.py

import myproject.myapp.service.linepattern.cornermanager as cm
import myproject.myapp.service.linepattern.line as cl

class Corner:
    def __init__(self, cornerNumber: int, cornerManager: cm.CornerManager) -> None:
        self.cornerNumber = cornerNumber
        self.cornerManager = cornerManager
        self.lineSet: Set[cl.Line]
        
    def initializeLines(self) -> None:
        #Add lines to the corner including its neighbor
        if self.cornerNumber == 1:
            self.lineSet.add(cl.Line(self, self.cornerManager.corner(2)))
        elif self.cornerNumber == 2:
            self.lineSet.add(cl.Line(self, self.cornerManager.corner(2)))

    
    def addLine(self, line: cl.Line) -> None:
        #Remove line from both corners
        for corner in cl.cornerPair():
            corner.lineSet.add(line)
        
    def removeLine(self, line: cl.Line) -> None:
        #Remove line from both corners
        for corner in cl.cornerPair():
            corner.lineSet.remove(line)

line.py

import myproject.myapp.service.linepattern.corner as cc

class Line:
    def __init__(self, cornerRight: cc.Corner, cornerLeft: cc.Corner) -> None:
        self.rightCorner = cornerRight
        self.leftCorner = cornerLeft        
    
    def cornerPair(self) -> Tuple[cc.Corner, cc.Corner]:
        #Return both corners
        return (self.rightCorner, self.leftCorner)

使用问题中的代码(三个独立的模块),通过在每个文件的顶部包含以下代码应该能够解决问题:

from __future__ import annotations

简而言之,此代码会将注释视为 strings。您可以在以下位置阅读更多相关信息: and Evaluation of annotations.

对于错误,这实际上是 Python 执行您的模块的方式(从 cornermanager.py 开始):

  1. import corner。使用此 Python 首先将模块保存在其内部模块 (sys.modules) 之前 运行 导入的模块代码。
  2. corner.py本身,前两行是import statement,所以让我们从import cornermanager开始。同样,Python 将模块保存在其内部模块中,然后执行 cornermanager.
  3. 中的代码
  4. 回到cornermanager.py。我们知道这个模块的第一行是 import corner,但是因为 Python 已经在它的内部模块中保存了这个模块 (corner),所以它不会第二次导入它。也就是说,Python 跳过它并执行此行下面的代码。有趣的是,因为 corner 除了第一行 (import cornermanager) 之外还没有执行它的所有代码,所以尝试获取它的 Corner class 不会成功。