Reputation: 47
I have got a problem with circular imports in Python 3.9. I know there are numerous articles, questions and answers about this topic and I really have read a lot of them and about how to circumvent but I cannot figure out why it happens in my case. As far as I have understood one can circumvent circular imports by doing absolute imports:
This is my Stacktrace:
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
and this is my file hierarchy
/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
and finally my code
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)
Upvotes: 0
Views: 216
Reputation: 752
With the code you put in the question (three separated modules), by including the following code at the top of each file should be able to fix the problems:
from __future__ import annotations
In short this code will treat annotations as strings
. You can read more about this at: Type hint of the enclosing class
and Evaluation of annotations
.
For the errors, this actually how Python executes your modules (start from cornermanager.py
):
import corner
. With this Python first saves the module in its internal module (sys.modules
) before running the imported module code.corner.py
itself, the first two lines are import statement
, so let's start with import cornermanager
. Again, Python saves the module in its internal module then executes the code in cornermanager
.cornermanager.py
. As we know first line of this module is import corner
, but because Python already saved this module (corner
) in its internal module, it doesn't import it the second time. That is, Python skips it and executes code below this line. Interestingly, because corner
hasn't executed all its code but the first line (import cornermanager
), attempt to fetch its Corner class
will not succeed.Upvotes: 1