Python

Modules and imports — the basics

How code in one Python file becomes usable from another — the mechanism behind every `import` statement you'll ever write.

Beginner

3 min read

A module is just a Python file

# math_helpers.py
def square(n):
    return n * n
 
def cube(n):
    return n * n * n

Any .py file is a module — there's no special syntax needed to "make" one. math_helpers.py defines two functions; any other file in the same project can now access them by importing that module by name (without the .py extension):

# main.py
import math_helpers
 
math_helpers.square(4)   # 16
math_helpers.cube(3)      # 27

import math_helpers runs the entire math_helpers.py file once, then makes everything it defined accessible through math_helpers.NAME — the module name acts as a namespace, keeping square and cube clearly associated with where they came from.

The different import styles, and when each makes sense

import math_helpers
math_helpers.square(4)
 
from math_helpers import square
square(4)                          # no module prefix needed
 
from math_helpers import square as sq
sq(4)                               # renamed on import
 
import math_helpers as mh
mh.square(4)                        # the module itself, renamed

import module_name keeps everything under the module's namespace (module_name.thing) — the safest default, since it's always clear where a name came from. from module_name import thing pulls a specific name directly into your file's own namespace, convenient when you use it constantly, but riskier if two different modules happen to define something with the same name. as renames whatever's imported — commonly used for long module names (import numpy as np is close to universal convention in that specific case) or to avoid a name collision.

Python's own standard library: modules that ship with Python

import math
math.sqrt(16)      # 4.0
math.pi              # 3.14159...
 
import random
random.randint(1, 10)   # a random integer from 1 to 10
 
import datetime
datetime.date.today()    # today's date

Python ships with a large standard library — modules for math, random numbers, dates, file paths, and much more — all available via import with nothing extra to install. Before writing your own implementation of something common, checking whether the standard library already has it is almost always worth the thirty seconds it takes.

Third-party packages: modules someone else wrote

pip install requests
import requests
response = requests.get("https://api.example.com/data")

Beyond the standard library, the wider Python ecosystem publishes packages through PyPI (the Python Package Index) — pip install PACKAGE_NAME downloads and installs one, after which it can be imported exactly like a standard library module or your own code. Django itself, imported as django throughout its own lessons, is one such package — nothing about import django is different in kind from import math_helpers.

if __name__ == "__main__": — the pattern you'll see in nearly every script

# math_helpers.py
def square(n):
    return n * n
 
if __name__ == "__main__":
    print(square(5))   # only runs when this file is executed directly

Every Python file has a built-in variable __name__. When a file is run directly (python math_helpers.py), __name__ is set to "__main__". When that same file is instead imported by another file, __name__ is set to the module's actual name ("math_helpers") instead. Wrapping code in if __name__ == "__main__": means it only runs when the file is executed directly — not when some other file imports it just to use its functions. This is why the pattern shows up constantly: it lets one file act as both a reusable module and a standalone script, without the "standalone script" part accidentally running every time something else imports it.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What makes a Python file a 'module'?

2. What's the main practical difference between `import module` and `from module import thing`?

3. What does `if __name__ == "__main__":` actually check?

4. Before writing your own implementation of a common utility (like generating a random number), what's usually worth checking first?