3

Possible Duplicate:
Circular (or cyclic) imports in Python

a.py

import b

class Abstract(object):
    pass

class Concrete(Abstract):
    def get_newthing(self):
        return b.NewThing()

(Note: It will be difficult for me to do any major refactoring of a.py)

b.py

import a
#reload(a)

class NewThing(a.Abstract):
    pass

As written, running "import b, a" works, but running "import a" gives

AttributeError: 'module' object has no attribute 'Abstract' 

as Python reaches the "import b" line in a.py and then while importing b tries to access "a.Abstract" which hasn't been created yet.

If I include the reload statement though, I can do "import a" just fine, as Python jumps back to the a.py module and creates the Abstract class before continuing in b.py. So it seems to work (although I should probably add a hasattr check before doing the reload).

I have been looking for ways to resolve this import loop issue and haven't seen any suggestions along these lines. Is there any pitfall in using reload() in this way?

Community
  • 1
  • 1
  • You should check out [this question on circular imports](http://stackoverflow.com/questions/744373/circular-or-cyclic-imports-in-python). – Andy Hayden Dec 15 '12 at 02:19

2 Answers2

0

Don't use reload for this, it's only for use in interactive prompts. You can fix this circularity like this:

class Abstract(object):
    pass

class Concrete(Abstract):
    def get_newthing(self):
        import b
        return b.NewThing()

Even better would be to refactor your code so you don't need circular imports.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • The `import b` statement can be at module level as long as it's below the definition of `Abstract`. – Blckknght Dec 15 '12 at 02:24
  • @Blckknght: if you have an answer, you should write it as an answer. – Ned Batchelder Dec 15 '12 at 02:25
  • Can you give more information on why reload should not be used in this way? – user1905527 Dec 15 '12 at 03:32
  • `reload` simply wasn't meant for use other than in the interactive interpreter. The third sentence in the docs is: "This is useful if you have edited the module source file using an external editor and want to try out the new version without leaving the Python interpreter." There are other solutions that work, use them. – Ned Batchelder Dec 15 '12 at 12:39
0

The design of your a.py module is very bad. It forces you to have a circular import, which is usually something to avoid. The best solution would be to split the Concrete class (and the import b line it requires) out into a separate module, which can import both a and b without any circularity.

However, if that's too much refactoring for your situation, you might try moving the import b line from the top of a.py to a point below the definition of Abstract. This will fix the error you're getting, as it ensures that NewThing will always be able to see Abstract's definition.

That is, do:

class Abstract(object):
    pass

import b

class Concrete(Abstract):
    def get_newthing(self):
        return b.NewThing()

This is a minimal change, but it should work for this situation. If Concrete needed definition-time access to the NewThing class though, it wouldn't be possible to fix this way.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • There are other modules that a.py imports that in turn import b. So I'd have to move all of those down below Abstract. Seems like it would be difficult to maintain. – user1905527 Dec 15 '12 at 03:35
  • Moving Abstract to a separate file seems like a good idea. However, I'm not really the owner of a.py and that seems like a pretty major thing to do (that's the reason why I'd prefer a solution that doesn't modify a.py). Any reason why reload() is a bad idea? – user1905527 Dec 15 '12 at 03:42
  • BTW I also suggested moving NewThing into a.py, but others on the team want to keep it in a separate module. – user1905527 Dec 15 '12 at 03:53