You certainly can import child
from parent
and parent
from child
. The key to making this work is for child
not to probe too deeply into parent
from its module-level code, because the parent
module is only partly loaded at the time that the Python runs the module-level code in child
.
Here's what happens when you import child
from parent
and parent
from child
(assuming parent
is loaded first):
- Code at the module level of
parent
runs until it reaches a statement that loads child
(either import child
or from child import something
). By "module level", I mean the statements that aren't within a class or function definition. The classes and functions defined at the module level will also be created as objects within the module. However, the functions and class methods themselves will not be run yet.
- When Python gets to the
import child
statement (or equivalent) in parent
's module-level code, it will stop running the parent
code and begin running the module-level code in child
. If child
imports parent
via import parent
or from parent import something
, it will get the parent
module in its current, partially constructed state. So module-level code in child
cannot access objects that are defined below import child
in parent.py
.
- Once
child
's module-level code finishes running, control will return to parent
below the import child
statement, and Python will finish running all of the module-level code in parent
.
This process will give you trouble if child
's module-level code tries to access objects that are defined in parent
after the import child
statement (since parent
is only partially finished when child
loads). The solution to this is to import parent
at the module level of child
but defer accessing objects within parent
from child
until after child
and parent
have finished loading. In particular, instead of using from parent import something
in child
's module-level code, you may need to use import parent
, then access parent.something
from inside function or method code in child
. This is safe to do because those functions and methods won't be run until after child
and parent
finish running, at which point all the elements of the parent
module are correctly defined.
Here's an example of what I mean, using the setup you described in a comment. If you give more information on the code that is giving you problems, I could tailor this more closely.
Version 1 (won't work)
__main__.py:
from user import User
u = User()
user.py:
from data_manager import DataManager
...
class User:
def __init__(self, data_manager=None):
if data_manager is None:
data_manager = DataManager(user=self)
self.data_manager = data_manager
data_manager.py:
# next line will fail because user.py has been partly loaded
# and user.User doesn't exist yet
from user import User
...
class DataManager:
def __init__(self, user=None):
...
if user is None:
user = User(data_manager=self)
self.user = user
Version 2 (will work)
__main__.py:
from user import User
u = User()
user.py:
import data_manager as dm
...
class User:
def __init__(self, data_manager=None):
if data_manager is None:
data_manager = dm.DataManager(user=self)
self.data_manager = data_manager
data_manager.py:
import user as user_module
...
# this defines a class which will eventually create an instance
# of user.User, but it won't try to do that until control returns
# to __main__.py, by which point the `user` and `data_manager`
# modules are fully defined
class DataManager:
def __init__(self, user=None):
...
if user is None:
user = user_module.User(data_manager=self)
self.user = user
Note that references in your classes' __init__
methods don't get resolved until the class is actually instantiated. i.e., the user = user_module.User(data_manager=self)
line does something like this: "look in the current module for an object called user_module
; look in that object for a User
attribute; construct an object of that class". The important thing is that data_manager
can safely import the user
module early on (the module exists already, even though it is only partly constructed), but the code above won't actually look for anything inside the user
module until a DataManager
object is instantiated, by which time user.User
will be properly defined.