Python circular import when accessing method from class in separate module

Viewed 239

I have two modules and each module contains a class. In each module I want to reference methods from the class in the other module. I have setup a small class to help wrap my head around the concept. I imported one module into the other module, but I am still getting a circular import error. To my understanding this is the proper way to do it, but I am still getting an error. Here are my example classes:

a.py module:

import b
class A():
    def __init__(self):
        print("A has run")
    def aa():
        print("aa has run")

b.B.bb()

b.py module:

import a
class B():
    def __init__(self):
        print("B has run")
    def bb():
        print("bb has run")
        # Run method from class in seperate module
        a.A.aa()

Here is my error: AttributeError: partially initialized module 'b' has no attribute 'B' (most likely due to a circular import)

3 Answers

If a.py is importing b.py and b.py is importing a.py, that's a circular import. Typically you want to restructure your code so that your modules do not need to import each other. Without seeing more about your actual use case, it's hard to give advice, but it could be something like this:

a.py module:

class A():
    def __init__(self):
        print("A has run")
    def aa():
        print("aa has run")

b.py module:

import a
class B():
    def __init__(self):
        print("B has run")
    def bb():
        print("bb has run")
        # Run method from class in seperate module
        a.A.aa()

c.py module

import b
b.B.bb()

You are right, that's due to circular import, and the reason is your import is at module level, instead if you exclude the module level import, you will not run in an issue, for example:

Modify your code like this:

class A():
    def __init__(self):
        print("A has run")
    def aa():
        print("aa has run")

if __name__ == '__main__':
    import b
    b.B.bb()

OUTPUT

bb has run
aa has run

Now, you'll no longer get the AttributeError because now the module b is not being imported at module level in module a.

Bit of a hack but the easiest (if hacky) way to get out of this is

class B():
    def __init__(self):
        print("B has run")
    def bb():
        print("bb has run")
        import a  # import on demand (each time)


        # Run method from class in seperate module
        a.A.aa()

A slightly better approach is to remember your import of a:

import b
class A():
    def __init__(self):
        print("A has run")
    def aa():
        print("aa has run")


print("running A")
b.B.bb()
b.B.bb()

class B():
    def __init__(self):
        print("B has run")
    def bb():
        print("bb has run")
        #remember your import of a
        a = getattr(B, "a", None)
        if not a:
            import a 
            B.a = a 

        # Run method from class in seperate module
        a.A.aa()

You'll notice that "running A" only happens twice: once when you python a.py and then the first time b.B.bb() gets called and imports on demand.

Related