Hacker News new | ask | show | jobs
by lp251 1794 days ago
Suppose you have two classes, A and B. They are sufficiently complex to merit their own modules.

Suppose you have some method of A which does something special if it gets an instance of B, and vice versa. Now you have a circular import problem; glhf

5 comments

I generally solve this problem by having a module specifically containing the abstract base classes of each of the classes I will be working with that implements either no or bare minimum functionality for these objects. That way, any other module can import this module and have visibility of every other class I will be working with.
Won't this work just fine if you instead of writing:

  from a import A
write

  import a
and in the code (which is presumably not at module level) check against

  isinstance(obj, a.A)

?
> Now you have a circular import problem; glhf

But why not collapse into a single module at this point if you can’t avoid dependency? What are the separate modules adding at this stage forward?

You can, but sometimes it’s not ideal.

I ran into this when A and B had many derived classes. I wanted to put A and it’s derived classes in one module, and B and it’s derived classes in another. It was messy.

I wound up putting A and B in a single module and having a separate one for the derived classes. Not ideal.

It does sound ideal to me, or at least better than the initial proposal.

A and B both need to know about the other's base definition, neither cares about the details about the other's derived classes. Splitting it into three modules shares as little surface area as possible.

> Suppose you have some method of A which does something special if it gets an instance of B.

While that’s in rare circumstances the right thing to do, it's mostly an anti-pattern—you should be taking an object supporting a protocol, with the behavior difference depending on a field or method of (or actually implemented in a method of) that protocol. If you do that, you don't create a dependency on a concrete class that happens to require the special behavior.

IMHO that's code smell. Modules shouldn't depend on each other, because that creates a web of tangled dependency where you have to understand everything before you can understand one of them. Circular dependency is to modules what goto is to control flow.

Besides, if you are in a "well, fuck it, deadline is tomorrow" mode, you can always do something horrible like:

    if 'classB' in type(obj).__name__: ...
I think bad code gives raise to more dependencies in general and so circular dependencies.

But the truth is sometimes it has happened to me and the only solution I found was creating an small module with maybe one or two functions which is not exactly ideal.