0

We chose python for a new project because we wanted to use a language where we could write beautiful code in non-verbose way.

Our consultant we used to write the code has delivered a great working solution. But when we look at the code, it's riddled with function local from X import Y. We promptly moved the imports to the top of the files but are now stricken with circular dependencies. We have absolutely no wish to resolve the circular dependencies and we have no wish to move the imports back to the functions, which is extremely verbose.

Question 1: How do we resolve this?

Question 2: what is this circular dependency non-sense? How can the Python community accept this when other languages seems to have solved this just fine? (I hope it isn't regarded as a feature of some kind)

Per Eriksson
  • 524
  • 1
  • 9
  • 25
  • Also related: [How to avoid circular imports in Python?](http://stackoverflow.com/questions/7336802/how-to-avoid-circular-imports-in-python), [Circular (or cyclic) imports in Python](http://stackoverflow.com/questions/744373/circular-or-cyclic-imports-in-python) – poke Oct 14 '16 at 05:40
  • Working code is not necessarily well-designed code. Your consultant used local imports because they had circular dependencies. But circular dependencies usually are a sign of code smell. – poke Oct 14 '16 at 05:43
  • @poke "circular dependencies usually are a sign of code smell" That must surely be a Pyhton specific opinion. Never had any such problems in Java/C#/Swift – Per Eriksson Oct 14 '16 at 05:48
  • Again, just because it works, that doesn’t mean it’s well designed. Just one example: [What's wrong with circular references?](http://programmers.stackexchange.com/q/11856/2762). That applies to other languages as well. – poke Oct 14 '16 at 05:51
  • "other languages seems to have solved this just fine" - other languages have not solved the problem of circular dependencies. Circular dependencies will probably always be a headache. However, Python's `from` imports work in an unusual way that makes it easier than it should be to get yourself into cyclic dependency issues. – user2357112 Oct 14 '16 at 05:52
  • [Relevant Python FAQ entry.](https://docs.python.org/2/faq/programming.html#how-can-i-have-modules-that-mutually-import-each-other) – user2357112 Oct 14 '16 at 06:00
  • Honestly, I only slightly agree with the last point "Circular references in general are simply confusing and drastically increase the cognitive load when attempting to understand how a program functions." As for the rest of "whats-wrong-with-circular-references" I don't care much for. Java with Spring has Dependency Injection and I can add all the circular imports in Java as I want. – Per Eriksson Oct 14 '16 at 06:00
  • I always make sure I split the code into different files for different "domains". Onle file for helper funktions, one file for db functions, one file for each model enity and so on. It makes each file consistent and easy to follow. This circular dependency totally breaks that workflow – Per Eriksson Oct 14 '16 at 06:03

2 Answers2

3

Circular dependencies are caused by A importing B and B importing A. The usual solution is to make a C which imports B and A so that A and B don't have to import each other. You could also concatenate the two files if they're too tightly coupled.

This is a problem in almost all languages; it's harder to parse code with circular dependencies, so most languages restrict it to a directed acyclic graph.

By importing modules inside functions, you avoid a circular dependency. Somewhat. First A imports B, then, when the function is called, B can import A. Since you have to call the function for B to import A, you don't get the same A imports B imports A imports B ... loop.

Filip Haglund
  • 13,919
  • 13
  • 64
  • 113
2

We handle circular dependencies because, well, we usually don't create them. It's not exactly common when two modules require each other.

If you can't refactor your A and B modules into a common module C that both import, you should just use local imports for one of the modules, only in the functions that require it.

But really, you'll be better off modifying your modules.

zmbq
  • 38,013
  • 14
  • 101
  • 171