質問

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.

  1. When I use import directory in file.py, and import file in directory.py, it only works when my working directory is filesystem (that is, when the imports are local).
  2. When I use import filesystem.directory in file.py, and import filesystem.file in directory.py, it works fine, except the aesthetic nuisance of writing filesystem.file.File and filesystem.Directory.directory all them time.
  3. Curiously, when I use import filesystem.directory as directory or from filesystem.directory import Directory, I get the circular import error 'module' object has no attribute 'directory'. My guess is that while import ... is lazy, import ... as and from ... import attempt to evaluate the module and notice the circularity immediately.
  4. 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:

  1. How would you design a filesystem, where a file class uses the directory class, and vice versa?
  2. 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.

役に立ちましたか?

解決

Using a fully-qualified path isn't really a hack; it's probably the proper solution to this particular issue. If you want a shorter name to type out, you could do something like this:

import filesystem.directory

class File(object):

   def __init__(self):
       self._Directory = filesystem.directory.Directory

   def foo(self):
       some_dir = self._Directory(...)

This also makes it trivial to swap in a mock if you're testing, et cetera.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top