DEV Community

Prahlad Yeri
Prahlad Yeri

Posted on

Recursive imports is the Achilles' heel of Python

Python is a beautiful language syntactically and is also good for readability and maintainability. But when it comes to weaknesses, I think it's biggest drawback is recursive imports.

This weakness stems from the very idea of having modules for re-usability which no other popular language has to my knowledge. In some like java and C#, everything is a class, so this kind of problem never occurs. In others like PHP, procedural code is allowed but they use the simple require or include constructs to add reusable code.

With python, there are many circumstances where X module imports Y and at the same time, the Y module needs to import an object from X. This is a very common scenario once you start developing complex apps. For example, this could be seen even in a very basic flask app which has its own package (lets say app), an init module (__init__.py) and a routes module (routes.py), something like this:

└───app
    │   __init__.py
    │   routes.py
Enter fullscreen mode Exit fullscreen mode

This is a very simple flask app where I define the actual flask object in the init module:

# c:\source\app\__init__.py
from flask import Flask
from app import routes

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

Now, let me define the routes.py as follows:

# c:\source\app\routes.py
from app import app

@app.route('/')
@app.route('/index')
def index():
    return "Hello, World"
Enter fullscreen mode Exit fullscreen mode

The problem is that this code won't work! For the simple reason that init module tries to import routes but the routes module will fail (simply because the routes module tries to import the app.app object from init module which hasn't been initialized yet).

This is the problem of recursive import. To fix this, I'll have to ensure that the routes module is always imported after the app object has been initialized as follows:

# c:\source\app\__init__.py
from flask import Flask

app = Flask(__name__)

from app import routes
Enter fullscreen mode Exit fullscreen mode

As you can see, recursive imports can be tricky to deal with in an otherwise structured and seemingly easy language like python.

Top comments (1)

Collapse
 
unfamiliarplace profile image
Luke Sawczak • Edited

...And God help you if the classes actually need each other. For example, suppose I'm modelling the grammar of a language that has recursive operations, e.g.

X -> (X + X)
X -> (X + Y)
X -> (Y + X)
Y -> (X + X)
Y -> (X + Z)
...etc.

If I define X and Y as distinct classes in separate files, then X will need to reference Y and Y will need to reference X. I can't think of a non-hacky way to do this.