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?

1

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

The import b statement can be at module level as long as it's below the definition of Abstract.
@Blckknght: if you have an answer, you should write it as an answer.
Can you give more information on why reload should not be used in this way?
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.
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.

3 Comments

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.
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?
BTW I also suggested moving NewThing into a.py, but others on the team want to keep it in a separate module.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.