Modules Introduction
Objectives
By the end of this chapter, you should be able to:
- Define what a module is
- Import custom and built-in modules
- Explain the purpose of the
if __name__ == "__main__"pattern - Organize multiple modules into a package with
__init__.py
๐ก Why this matters: As a program grows, keeping everything in one file gets unmanageable fast. Modules are how you split code across files and reuse it wherever you need it.
Writing Your Own Module
A module is just a Python file whose code you can import elsewhere. Say you write a file called helpers.py:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
From another file in the same directory, app.py, you can pull those functions in directly:
from helpers import add, subtract
def calculate_numbers(a, b, fn):
if fn == "add":
return add(a, b)
elif fn == "subtract":
return subtract(a, b)
calculate_numbers(1, 4, "add") # 5 (calls add, which came from helpers.py)
Different Ways to Import
import random # the whole module: call functions as random.something()
random.random()
import random as r # the whole module, aliased to a shorter name
r.random()
from random import random # just one function, called directly
random()
from random import random as r # one function, aliased
r()
A few more patterns you’ll see:
import math as m
from math import sqrt
from my_module import only_what_i_need
from my_module import * # everything from my_module: avoid this in real code
Convention: put your import statements at the top of the file, and prefer importing exactly what you need rather than everything. from my_module import * is especially worth avoiding: it dumps every name from that module into your file, making it hard to tell where a given name actually came from.
The if __name__ == "__main__" Pattern
__name__ is a special variable Python sets automatically. It equals "__main__" only when a file is run directly, not when it’s imported. That lets a module keep code that should run when executed on its own, but not when someone else imports it.
helper.py:
print("I'm from the helper file!")
def display(name):
print("My name is " + name)
display(__name__)
if __name__ == "__main__":
print("I'm the helper file and was loaded directly!")
Run python3 helper.py directly, and it prints all three lines, since __name__ really does equal "__main__" here:
I'm from the helper file!
My name is __main__
I'm the helper file and was loaded directly!
Now import it from other.py:
import helper
print("I'm from the other file!")
helper.display(__name__)
if __name__ == "__main__":
print("I'm the other file and was loaded directly!")
Running other.py prints:
I'm from the helper file!
My name is helper
I'm from the other file!
My name is __main__
I'm the other file and was loaded directly!
Inside helper.py, __name__ is "helper" this time, not "__main__", so its if __name__ == "__main__": block never runs. Inside other.py, __name__ really is "__main__", since that’s the file you executed directly.
Organizing Multiple Modules into a Package
A single file is a module; a folder of related modules is a package. To turn a plain folder into a package, add a file named __init__.py inside it. Even an empty one is enough to make Python treat the folder as importable:
shapes/
โโโ __init__.py
โโโ circle.py
โโโ square.py
# shapes/circle.py
def area(radius):
return 3.14159 * radius ** 2
# shapes/square.py
def area(side):
return side ** 2
From outside the shapes folder, you can import from it just like any other module, using dot notation to reach inside:
from shapes import circle, square
circle.area(2) # 12.56636
square.area(3) # 9
__init__.py can also be used to control exactly what a package exposes when it’s imported. For instance, it can re-export specific functions so callers don’t need to know your internal file layout:
# shapes/__init__.py
from .circle import area as circle_area
from .square import area as square_area
from shapes import circle_area, square_area
circle_area(2) # 12.56636
You’ve actually already used packages without necessarily noticing: beautifulsoup4 and requests, installed with pip3 back in Module 1, are both organized this exact way internally.
Try It
- Create a small module with two functions, then import and use them from a separate script.
- Add an
if __name__ == "__main__":block to that module, and confirm it runs when you execute the module directly but not when you import it elsewhere. - Try
from your_module import *versus importing one specific function, and notice the difference in what ends up in your namespace. - Turn a folder containing two related modules into a package with
__init__.py, and import from both using dot notation.
Recap
- A module is just a
.pyfile whose functions (and other code) you canimportelsewhere. import module,import module as alias,from module import name, andfrom module import name as aliasare the main import styles: prefer importing exactly what you need overimport *.if __name__ == "__main__":runs its block only when a file is executed directly, not when it’s imported.- A folder becomes an importable package once it contains an
__init__.pyfile, which can also re-export specific names so callers don’t need to know your internal file layout.
Next lesson: a tour of some of Python’s most useful built-in modules.