Brian Mearns
2 min readApr 17, 2018

Ok, I think I understand your point. In python, modules can exist as objects which can be referenced without relying on their dependencies. Thus if two modules are “dependent” on one another, there may be no issue with creating both modules, and giving each a reference to the other.

I believe the key here is that modules can essentially be partially loaded. When module A is first loaded, the runtime creates it as an essentially empty object. The module is then executed and populates its namespace with global variables and functions, which become attributes on the module object.

If module A imports module B as part of its execution, then the module A object may only be partially defined at the time, but it still exists as an object, so if module B tries to import it, it will be able to do so. The runtime sees that module A already exists and doesn’t try to re-load it, it simply gives module B the module A object, partially defined though it may be.

If we consider object instantiation in java to be somewhat analogous to module loading in python, this points to a potential solution that I think you hinted at: if we can allow our objects to be instantiated without their dependencies, then we can defer resolution of the cyclic dependency to a point where it may no longer be a cycle.

This is a useful strategy in some cases, where the dependencies are only in conflict because you’re trying to create them both at the same time. This might be the case, for instance, where two objects are dependent on one another only because, at some point, they need to be able to invoke methods on one another, or in some other way send each other messages. In this case, they can both be instantiated without references to one another, or with incomplete references to one another a la the python modules.

In all likelihood, we could have resolved the circular dependency we faced in a similar way, by instantiating the objects in isolation and then passing in a reference after the fact. There are some added complications with this; it would likely have meant that the objects needed to be mutable, and that we would need protections against using a field which hadn’t yet been set.

A change like this could leave you with what I would consider partially-instantiated objects and transient state. You can actually see this limitation in python module loading, because this strategy only works as long as one of the modules is able to fully execute (i.e., to define itself) with the other module only partially defined. Otherwise, you run into what is ultimately still a circular dependency, but which manifests as a missing attribute error, as seen in this gist.

Deferment is definitely a good strategy to have in your toolbox but, as with all things, it’s good to understand the cost and limitations of it.

Thanks for bringing this up, I hadn’t drawn parallels with module loading although, in retrospect, the correlation is obvious.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Brian Mearns
Brian Mearns

Written by Brian Mearns

Software Engineer since 2007 ・ Parent ・ Mediocre Runner ・ Flower and Tree Enthusiast ・ Crappy Wood Worker ・ he/him or they/them

Responses (1)

Write a response