I'm using Python 3.3.2 to write a package that encapsulates a filesystem. My project looks like this:
~/
python/
filesystem/
__init__.py
file.py
directory.py
With PYTHONPATH=~/python
.
The problem is, file.py
needs directory.py
(for example, for File.get_directory()
), and directory.py
needs file.py
(for example, for Directory.get_files()
), so I have a circular import.
- When I use
import directory
infile.py
, andimport file
indirectory.py
, it only works when my working directory isfilesystem
(that is, when the imports are local). - When I use
import filesystem.directory
infile.py
, andimport filesystem.file
indirectory.py
, it works fine, except the aesthetic nuisance of writingfilesystem.file.File
andfilesystem.Directory.directory
all them time. - Curiously, when I use
import filesystem.directory as directory
orfrom filesystem.directory import Directory
, I get the circular import error'module' object has no attribute 'directory'
. My guess is that whileimport ...
is lazy,import ... as
andfrom ... import
attempt to evaluate the module and notice the circularity immediately. - One way to solve this is to
import filesystem.directory
inside the functions that use it. Unfortunately, many of my methods use it, and importing it inside a class does not seem to work.
This is solvable, of course: sucking it up and writing filesystem.directory.Directory
; assigning an __import__
to a global variable in the __init__
method for all the other methods to use; defining File
and Directory
in the same file; and so on. But these are more compromises than solutions, so my questions still stand:
- How would you design a filesystem, where a file class uses the directory class, and vice versa?
- And more generally, how would you deal with (or avoid) circular imports?
Thanks.
UPDATE [03.07.2013] (Mostly for discussion's sake)
Another solution I came upon is some sort of forward declarations, with empty file
and directory
classes in a common header, followed by separate implementations (more like attributes addition). While the resulting design is very neat, the idea is more C++-ish than Pythonic.