How to Avoid Circular Imports in Python

How to avoid circular imports in Python?

Only import the module, don't import from the module:

Consider a.py:

import b

class A:
def bar(self):
return b.B()

and b.py:

import a

class B:
def bar(self):
return a.A()

This works perfectly fine.

How to avoid circular import looping

You have a few options:

  1. put all your code in one file, if it needs to be this tightly linked
value = 5

def set():
value = 6

set()

  1. put the code that uses parts from the others in a third file, or in other words break up your code so you avoid the circular references - your example is a bit too simple to make that work though.

  2. pass values to functions you want to use, or pass objects you want to modify; importing and modifying another module's globals isn't a very good way to design your program anyway:

# this is my_module.py

def set_value(x):
x.value = 6

def increment(x):
return x + 1

And elsewhere:

import my_module

class X:
value = 5

x = X()
my_module.set_value(x)

y = 5
y = my_module.increment(y)

Avoiding circular imports with type annotations in situations where __future__.annotations is insufficient

In the most cases using typing.TYPE_CHECKING helps to resolve circular import issues.

# a.py
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from b import B

class A: pass
def foo(b: B) -> None: pass
# b.py
from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from a import A

class B: pass
def bar(a: A) -> None: pass
# __main__.py
from a import A
from b import B

However, for exactly your MRE it won't work. If the circular dependency is introduced not only by type annotations (e.g. your type aliases), the resolving may become really tricky.

If you don't need Foo available at runtime in your example, it can be declared in if TYPE_CHECKING: block too, mypy will interpret that properly. If it is for runtime too, then everything depends on exact code structure (in your MRE dropping import b is enough). Union type can be declared in separate file that imports a, b and c and creates Union. If you need this union in a, b or c, then things are a bit more complicated, probably some functionality needs to be extracted into separate file d that creates union and uses it (also the code will be a bit cleaner this way, because every file will contain only common functionality).

Avoiding circular (cyclic) imports in Python?

Merge any pair of modules that depend on each other into a single module. Then introduce extra modules to get the old names back.

E.g.,

# a.py
from b import B

class A: whatever

# b.py
from a import A

class B: whatever

becomes

# common.py
class A: whatever
class B: whatever

# a.py
from common import A

# b.py
from common import B

What happens when using mutual or circular (cyclic) imports in Python?

There was a really good discussion on this over at comp.lang.python last year. It answers your question pretty thoroughly.

Imports are pretty straightforward really. Just remember the following:

'import' and 'from xxx import yyy' are executable statements. They execute
when the running program reaches that line.

If a module is not in sys.modules, then an import creates the new module
entry in sys.modules and then executes the code in the module. It does not
return control to the calling module until the execution has completed.

If a module does exist in sys.modules then an import simply returns that
module whether or not it has completed executing. That is the reason why
cyclic imports may return modules which appear to be partly empty.

Finally, the executing script runs in a module named __main__, importing
the script under its own name will create a new module unrelated to
__main__.

Take that lot together and you shouldn't get any surprises when importing
modules.

How to avoid circular imports?

Dump the config.py file.
initialize your variables in vitals.py and change the variables from GUI.py.

i.e. inside vitals.py:

weight = 0

and inside GUI.py

import vitals
vitals.weight = weightVar.get()

Edit:

the new problem you now have is because your value of bmr depends on weight.

when you do

import vitals

bmr gets calculated with initialized value.
When you want to have bmr calculated dynamically you need to change it to a function. So in vitals.py:

def bmr(weight, height, age):
return Decimal(((10 * float(weight)) + (6.25 * height) - (5 * age) + 5))

And then you call it from GUI.py:

import vitals
vitals.weight = weightVar.get()
print(vitals.bmr(vitals.weight, vitals.height, vitals.age))

edit2:

you need to do this for all values that are dependent on other variables



Related Topics



Leave a reply



Submit