Python
Programming
Code Style
Best Practices
Software Development

Should import statements always be at the top of a module?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Python, imports are generally placed at the top of a module for readability and maintainability, as recommended by PEP 8. But “always” is too absolute. There are legitimate cases for local imports: avoiding circular dependencies, reducing optional dependency startup cost, or delaying heavyweight imports until needed.

The right rule is default-to-top, then break intentionally with clear justification.

Core Sections

1) Why top-level imports are preferred

Top-level imports make dependencies visible immediately and fail fast if environment setup is broken.

python
1import json
2from pathlib import Path
3
4
5def load_config(path: Path) -> dict:
6    return json.loads(path.read_text())

This is easier to reason about during code review and static analysis.

2) Legitimate local import: optional dependency

python
1def export_plot(data):
2    import matplotlib.pyplot as plt  # imported only when plotting feature is used
3    fig, ax = plt.subplots()
4    ax.plot(data)
5    return fig

This avoids importing heavy libraries for code paths that do not need them.

3) Legitimate local import: circular dependency mitigation

python
1# module_a.py
2
3def build_service():
4    from module_b import ServiceImpl
5    return ServiceImpl()

Local imports can break import cycles, but this should trigger architectural review. Long-term fix is usually refactoring shared abstractions.

4) Performance nuance

Python caches imports in sys.modules, so repeated local import statements are usually cheap after first load. Still, excessive local imports in hot loops hurt readability and can add overhead in tight paths.

python
def hot_path(items):
    # avoid imports in tight loops unless absolutely necessary
    return [item * 2 for item in items]

5) Tooling and style conventions

Use import sorting tools (isort) and linters (ruff, flake8) to enforce consistent grouping and order for top-level imports. When using local imports intentionally, add a brief comment so future maintainers know it is deliberate.

6) Team policy recommendation

Adopt a simple policy:

  1. default all imports to top of module,
  2. allow local imports only with one-line rationale,
  3. revisit local imports periodically to remove workarounds after refactors.

This gives consistency without blocking practical engineering tradeoffs.

7) Production checklist for Python import policy

Treat this topic as an operational concern, not only a coding snippet. Start by defining one explicit success metric that reflects business behavior, such as failed request rate, pipeline lag, model quality drift, or user-visible latency. Then create a small acceptance checklist that can run in both staging and production-like test environments. The checklist should verify the happy path, at least one failure path, and one boundary case.

Capture configuration assumptions close to the implementation, including timeouts, versions, environment variables, and external dependencies. If behavior varies by environment, encode those differences in configuration rather than hardcoded branches. Add lightweight observability from day one: key counters, error categorization, and structured logs with identifiers that support correlation during incident response.

Finally, define rollback and ownership before rollout. Decide who responds to alerts, what threshold should trigger rollback, and which fallback mode keeps the system functional if this component degrades. A clear ownership and rollback plan turns isolated technical knowledge into a maintainable production practice.

Common Pitfalls

  • Treating “imports at top” as dogma and ignoring real circular or optional dependency constraints.
  • Hiding critical dependencies deep inside functions without explanation.
  • Using local imports as permanent fixes for poor module boundaries.
  • Placing imports inside tight loops where overhead and noise accumulate.
  • Inconsistent import ordering that causes merge churn and style drift.

Summary

Import statements should usually be at the top of a Python module because clarity and dependency visibility matter. Local imports are valid for specific technical reasons, but they should be intentional, documented, and revisited. A balanced policy keeps codebase consistency while preserving flexibility for edge cases.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.