Python
Import Statement
Code Practices
Programming Best Practices
Code Readability

Why is import bad?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

from module import * (wildcard import) imports every public name from a module into your current namespace. It is considered bad practice because it pollutes the namespace, hides where names come from, causes silent name collisions, and makes code unpredictable for both humans and tools. PEP 8 explicitly discourages it for production code.

What import * Does

python
1# Imports ALL public names from math into the current namespace
2from math import *
3
4# Now you can use sin, cos, pi, etc. without prefix
5print(sin(pi / 2))  # 1.0
6print(ceil(4.2))    # 5

"Public names" means everything in the module's __all__ list, or if __all__ is not defined, every name that does not start with an underscore.

Problem 1: Namespace Pollution

python
1from os.path import *
2from posixpath import *
3
4# Both modules export 'join', 'split', 'exists', etc.
5# Which 'join' is this? The last import wins silently.
6join("a", "b")  # posixpath.join, not os.path.join

With explicit imports, the collision is visible:

python
from os.path import join
from posixpath import join  # IDE warns: 'join' already imported

Problem 2: Readability — Where Did This Come From?

python
1from pandas import *
2from numpy import *
3from scipy.stats import *
4
5# 500 lines later...
6result = array([1, 2, 3])  # numpy.array? pandas.array?
7mean_val = mean(result)     # numpy.mean? scipy.stats.mean?

A reader (or your future self) cannot determine which module provides array or mean without checking every wildcard import. With explicit imports:

python
1import numpy as np
2import pandas as pd
3
4result = np.array([1, 2, 3])  # Obviously numpy

Problem 3: Silent Name Shadowing

python
1from statistics import *
2
3# statistics exports 'mean', 'median', 'mode', etc.
4# But it also exports 'variance' — which shadows any local 'variance' variable
5
6variance = 100  # Your variable
7from statistics import *  # Silently overwrites 'variance' with the function
8
9print(variance)  # <function variance at 0x...> — not 100

Problem 4: Breaks Static Analysis

python
1from some_module import *
2
3# Linters (pylint, flake8, mypy) cannot determine:
4# - Which names are available
5# - Whether 'foo' is a function, class, or variable
6# - What types are expected
7foo(bar, baz)  # No autocomplete, no type checking

IDEs lose the ability to provide autocompletion, go-to-definition, and refactoring support.

Problem 5: Fragile Dependencies

python
1# version 1.0 of library
2from awesome_lib import *
3# Exports: process, transform, validate
4
5# version 2.0 adds a new export: 'open'
6from awesome_lib import *
7# Now 'open' shadows Python's built-in open()!
8open("file.txt")  # Calls awesome_lib.open, not built-in open

A library update can break your code without changing any of your own lines.

When import * Is Acceptable

python
1# 1. Interactive interpreter / Jupyter notebooks (exploration)
2>>> from math import *
3>>> sin(pi / 4)
4
5# 2. __init__.py to re-export a package's public API
6# mypackage/__init__.py
7from mypackage.core import *
8from mypackage.utils import *
9# This defines the package's public interface
10
11# 3. In __all__ controlled modules
12# mymodule.py
13__all__ = ['MyClass', 'helper_func']  # Only these are exported by *

Even in __init__.py, many projects prefer explicit re-exports for clarity.

The Right Way to Import

python
1# Option 1: Import the module (preferred for large modules)
2import numpy as np
3import pandas as pd
4
5np.array([1, 2, 3])
6pd.DataFrame({"a": [1, 2]})
7
8# Option 2: Import specific names (for small, specific usage)
9from datetime import datetime, timedelta
10from pathlib import Path
11from collections import defaultdict
12
13# Option 3: Aliased import for long names
14from matplotlib import pyplot as plt
15from sklearn.ensemble import RandomForestClassifier as RFC

Controlling __all__

If you maintain a module, define __all__ to limit what import * exports:

python
1# mymodule.py
2__all__ = ['public_func', 'PublicClass']
3
4def public_func():
5    pass
6
7class PublicClass:
8    pass
9
10def _private_helper():
11    pass
12
13def internal_but_exposed():
14    # Without __all__, this would be exported by import *
15    # With __all__, it is excluded
16    pass

PEP 8 Guidance

PEP 8 states:

Wildcard imports (from module import *) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools.

The only exception PEP 8 acknowledges is re-publishing an internal API as part of a public package interface.

Common Pitfalls

  • Built-in shadowing: Modules like os export open, and builtins names like filter, map, list can be shadowed by wildcard imports from libraries that define functions with the same names.
  • Circular imports: import * in circular dependency chains causes partially-initialized modules and ImportError or AttributeError at runtime.
  • Testing: Wildcard imports make mocking and patching harder because from module import * copies references — patching the original module does not affect the already-imported names.
  • __all__ is optional: If a module does not define __all__, import * imports everything without underscores. This can be hundreds of names from large modules.
  • Linter configuration: Enable F403 (wildcard import) in flake8 or wildcard-import in pylint to catch these automatically.

Summary

  • from module import * imports all public names, polluting the namespace
  • It hides where names come from, breaks IDE tooling, and causes silent name collisions
  • Library updates can introduce new names that shadow builtins or your own variables
  • Use import module or from module import specific_name instead
  • PEP 8 discourages wildcard imports in production code
  • Define __all__ in your own modules to control what gets exported

Course illustration
Course illustration

All Rights Reserved.