Modules and import

The forms of import, what each one puts in your namespace, and why import * is discouraged.

Overview

The three forms

import math            # math.sqrt(9)
from math import sqrt  # sqrt(9)
import numpy as np     # np.array(...)

The first keeps the module as a prefix. That is a feature: reading math.sqrt a hundred lines later tells you immediately where it came from, and it cannot collide with anything of yours.

The second is shorter and right when you use one or two names heavily and there is no ambiguity.

as renames on the way in, for length (numpy as np) or to avoid a clash with a name you already have.

imports.py

imports.py Python 3
Output

                    

import_care.py

import_care.py Python 3
Output

                    

Worth knowing

import math keeps the module name as a prefix, which shows where a function came from.
from math import sqrt puts sqrt straight into your namespace.
import * hides where names came from and silently overwrites your own.
Imports are cached: a module runs once per program, no matter how often it is imported.

Modules and import: A Practical Guide

A module is a file of Python. import runs it once and gives you access to what it defines. The forms of import differ only in what ends up in your namespace, and that difference matters more than it first appears.

import * and why not

from math import *

This pulls in every public name at once. Two problems, and the second is the serious one.

You can no longer tell where a name came from. sqrt(16) appears from nowhere, and finding its source means guessing which of the star-imports supplied it.

Worse, it silently overwrites. If you defined gamma and then star-import a module that also defines gamma, yours is gone with no warning at all — the page demonstrates precisely that, printing the function's own output before and after.

The place it is acceptable is an interactive session where you are exploring, and even there it is a habit worth not forming.

Imports are cached

Importing a module twice does not run it twice. Python keeps a table in sys.modules and hands back the same module object. So imports are cheap to repeat, and any code at the top level of a module runs exactly once per program — which is why putting slow work or side effects at module level is a trap.

Where imports go

At the top of the file, one per line, standard library first, then third-party, then your own. That is not aesthetics: an import buried inside a function runs on every call and hides a dependency from anyone scanning the file.

The standard library is large

Before installing anything, check what ships with Python. collections, itertools, datetime, json, random, statistics, pathlib and re cover an enormous amount of everyday work, and the page uses three of them in six lines.

What a module actually is

A module is a file, and importing it runs that file top to bottom, once. Every function definition, class definition and top-level statement executes at that moment, and the resulting names become attributes of the module object.

That single fact explains several things at once. It explains why top-level code in a module runs when someone imports it, which is why print statements left at module level surface in surprising places. It explains why circular imports are a problem: two modules each trying to finish running before the other can. And it explains the caching - having run it once, Python keeps the result.

The __main__ guard

Because importing runs the file, a script that does work at the top level does that work when imported too. The guard prevents it:

def main():
    ...

if __name__ == "__main__":
    main()

__name__ is "__main__" when the file is run directly and the module's name when it is imported. So the file can be both a usable script and an importable module, which is what makes it testable - a test can import main without running it.

Packages and relative imports

A directory with an __init__.py is a package, and modules inside it can import from each other relatively:

from . import helpers          # same package
from .models import Record     # a module in the same package

Relative imports only work inside a package and only when the package is being imported, not when a file inside it is run directly as a script. That restriction is behind a large share of ImportError: attempted relative import with no known parent package messages, and the usual answer is to run the module with python -m package.module rather than by path.

Where Python looks

Imports resolve against sys.path, which starts with the directory of the script being run, then the standard library, then installed packages. The first match wins, and that ordering is why a local file named random.py or json.py shadows the standard library module of the same name and produces errors that look impossible.

Naming a file after a module you also import is worth avoiding for exactly this reason - the failure is confusing out of all proportion to the mistake.

Import cost

Imports are cheap after the first, but the first one runs the whole module. A library that does substantial work at import time makes every program that imports it slower to start, which is why heavy setup belongs in a function the caller chooses to call rather than at module level.

Circular imports, and how to break one

Two modules that each import the other produce an error that reads as though something is missing when nothing is:

ImportError: cannot import name 'Order' from partially initialized module
'models' (most likely due to a circular import)

The mechanism follows directly from "importing runs the file". Module A starts running and hits from B import x. B starts running and hits from A import y — but A is only half-finished, so y does not exist on it yet. Python has A in sys.modules already, so it does not re-run it; it hands back the partial module, and the name is missing.

Three fixes, in order of preference.

Move the shared thing. If A and B both need Order, it belongs in a third module that both import and neither imports back. Most circular imports are a missing module rather than an import problem.

Import inside the function. Moving from B import x into the function that uses it delays it until both modules have finished loading. This works, and it hides a dependency from anyone scanning the file, so it is a fix rather than a design.

Import the module, not the name. import B and then B.x at call time often works where from B import x does not, because the attribute is looked up when used rather than when imported.

The one that does not work is reordering the imports, which usually moves the error rather than removing it.

Where installed packages come from

sys.path explains where Python looks; it does not explain how anything got there, and the gap is where most beginner import confusion lives.

pip install requests puts the package into the site-packages directory of whichever Python is running pip. If you have several Pythons — the system one, one from Homebrew, one from a virtual environment — then "pip installed it and Python cannot find it" almost always means two different interpreters. python3 -m pip install x avoids that entirely by installing into the interpreter you just named.

A virtual environment is a directory containing its own interpreter link and its own site-packages. python3 -m venv .venv creates one and activating it puts its interpreter first on your PATH, so pip and python both refer to it. The point is isolation: two projects can depend on incompatible versions of the same library without either breaking, and the set of packages a project needs is a property of the project rather than of your machine.

The practical rules are short. One environment per project. Record the dependencies in a file so the environment can be rebuilt. Never install into the system Python, which your operating system also uses. And when an import fails unexpectedly, python3 -c "import sys; print(sys.executable)" tells you which interpreter you are actually running, which resolves the question faster than anything else.

Organising modules of your own

A module is a file, so organising code is organising files, and a few conventions save a lot of trouble.

Group by what things *are for*, not by what they are. A models.py, views.py, utils.py split works until utils.py becomes the place everything lands. Splitting by feature — orders.py, billing.py — keeps related code together and keeps the imports between files shallow.

Keep module-level code to definitions. Anything that runs work, opens files or makes network calls at import time makes every importer pay for it, including your test suite. Put it in a function and let the caller decide.

Avoid naming a file after a module you also import. A local random.py shadows the standard library one for your whole program, and the resulting AttributeError: module 'random' has no attribute 'randint' looks impossible until you spot the file.

And watch the direction of dependencies. If A imports B, B should not need A. When it does, the two are really one module, or there is a third one waiting to be extracted — which is the same conclusion the circular-import section reached from the other direction.

Reading an import error

Four messages cover nearly every import failure, and each points somewhere specific.

ModuleNotFoundError: No module named 'requests' means Python looked along sys.path and found nothing. Either it is not installed, or it is installed for a different interpreter. Check with python3 -m pip list using the same python3 that failed.

ImportError: cannot import name 'X' from 'y' means the module was found and does not contain that name. Either it is a typo, or the version installed is older than the one you are reading about, or it is a circular import and the module is only half-loaded — the message says so when it can.

AttributeError: module 'x' has no attribute 'y' on a standard-library name is the shadowing case: a file of your own named x.py is earlier on the path than the real module. print(x.__file__) identifies it immediately.

ImportError: attempted relative import with no known parent package means a file inside a package was run directly by path. Run it as python -m package.module instead.

The common thread is that the message distinguishes "could not find the module" from "found it, could not find the name", and that distinction sends you to completely different places. Reading which of the two you have is most of the diagnosis.

Questions people ask

What is __init__.py for? It marks a directory as a package and runs when the package is imported. It can be empty, and since Python 3.3 a package without one mostly works — but including it avoids surprises.

Why does from . import x fail when I run the file? Relative imports need a parent package, which a file run by path does not have. Run it with python -m package.module.

Does import run the whole module? Yes, top to bottom, once per program.

How do I reload a module I changed? importlib.reload(module) in an interactive session. In a script, restart it — reloading has enough sharp edges that it is not worth relying on.

Where is a module actually loaded from? module.__file__ after importing it, which is the fastest way to confirm you have the one you meant.

Is import x inside a function slow? Only the first time. After that it is a dictionary lookup in sys.modules.

What is the difference between a module and a package? A module is a file; a package is a directory of modules. Both are imported the same way.

Can a module import itself? It can, and it gets the partially initialised version from sys.modules. There is no good reason to.

What does if TYPE_CHECKING: do? Guards imports that exist only for type hints, so they cost nothing at runtime and cannot cause a circular import.

Should I import inside a function to speed up startup? Only for genuinely heavy optional dependencies. For anything else the cost is already paid once and the hidden dependency is not worth it.

Recap in one screen

  • Importing runs the file once, top to bottom, and caches the result in sys.modules.
  • import x keeps the prefix and the provenance; from x import y is for one or two heavily used names; import * hides both and can silently overwrite.
  • The __main__ guard is what lets a file be both a script and an importable module.
  • Circular imports mean a shared piece belongs in a third module.
  • One virtual environment per project, and python3 -m pip to be certain which interpreter you are installing into.

Check yourself

0 of 3

Answer without scrolling back up.

  1. What is the main problem with `from module import *`?

  2. What does `import math` put in your namespace?

  3. Importing the same module twice does what?

Cheat sheet

Modules and import

A module is a file of Python. import runs it once and gives you access to what it defines. The forms of import differ only in what ends up in your namespace, and that difference matters more than it first appears.

PYTHON · vizlearn.in/python/modules_and_import.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.