Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove python circular import

user.py:

from story import Story

class User:
    ...
    def get_stories(self):
        story_ids = [select from database]
        return [Story.get_by_id(id) for id in story_ids]

story.py

from user import User

class Story:
    ...
    def __init__(self, id, user_id, content):
        self.id = id
        self.user = User.get_by_id(user_id)
        self.content = content

as you can see, there is a circular import in this program, which causes an ImportError. I learned that I can move the import statement in method definition to prevent this error. But I still want to know, is there a way to remove circular import in this case, or, is it necessary (for a good design) ?

like image 391
wong2 Avatar asked Nov 15 '13 07:11

wong2


2 Answers

Another way to mitigate the circularity is to change the import style. Change from story import Story to import story, then refer to the class as story.Story. Since you only refer to the class inside a method, it won't need to access the class until the method is called, by which time the import will have completed successfully. (You may have to make this change in either or both modules, depending on which one is imported first.)

The design does seem somewhat strange, however. Your design is such that the User and Story classes are very tightly coupled -- neither can be used without the other. In such a case, it would usually make more sense to have them both in the same module.

like image 176
BrenBarn Avatar answered Oct 19 '22 00:10

BrenBarn


The most obvious solution in this case is to break the dependency to the User class completely, by changing the interface so that the Story constructor accepts an actual User, not a user_id. This also leads to a more efficient design: for example if a user has many stories, the same object can be given to all of those constructors.

Other than that, the import of a whole module (that is story, and user instead of the members) should work - the module imported first will appear empty at the time when the second is imported; however it does not matter as the contents of these modules are not used at the global scope.

This is slightly preferable over importing within a method. Importing within a method has significant overhead over just a module-global lookup (story.Story), because it needs to be done for each method call; seems that in a simple case the overhead is at least 30-fold.