Avoid Circular Imports with TYPE_CHECKING
in Python ๐
TYPE_CHECKING
is a constant from the typing
module. It is True
during type checks and False
at runtime. This means you can import classes for type hints without causing circular imports at runtime.
When you use an import inside an if TYPE_CHECKING:
block, Python skips it at runtime. Yet type checkers like mypy
or pyright
will see it for verifying your annotations.
Basic Example ๐
# a.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from b import B # This import won't run at runtime
class A:
def link_to_b(self, b: 'B') -> None:
print('Linking A with B')
# b.py
class B:
pass
Here, A
refers to B
for type hints without importing it during runtime. That helps avoid circular imports.
Usage in an ORM ๐
# models.py
from typing import TYPE_CHECKING
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from database_setup import Base
if TYPE_CHECKING:
from other_models import RelatedModel
class MyModel(Base):
__tablename__ = 'my_model'
id = Column(Integer, primary_key=True)
related_id = Column(Integer, ForeignKey('related_model.id'))
# For type hints only:
related_model: 'RelatedModel' = relationship('RelatedModel')
This prevents runtime loops, because the if TYPE_CHECKING:
block loads only for type analysis.
Why Use It ๐
- Prevents circular imports by avoiding runtime loops between modules.
- Improves readability by separating type hints from the normal import flow.
- Helps type checkers by allowing tools to verify your types.
For more details, visit the Python docs on TYPE_CHECKING
.