Python's power comes from organizing code into reusable modules. Understanding how modules work is fundamental to writing professional Python code.
In this lab, you'll learn:
- How Python modules are just regular files
- Creating packages with
__init__.py - The
if __name__ == "__main__"pattern - Reading command-line arguments with
sys.argv
A Python module is simply a .py file containing Python code. That's it!
mkdir ~/python-modules
cd ~/python-modulesCreate math_utils.py:
Do not name your file math.py, as this will conflict with the existing math module.
"""
Math utility functions
Docstrings are special! They become the module/function documentation
and can be viewed with help() or pydoc.
"""
def add(a, b):
"""Add two numbers"""
return a + b
def multiply(a, b):
"""Multiply two numbers"""
return a * b
def power(base, exponent):
"""Raise base to the power of exponent"""
return base ** exponent
# This will run when the module is imported!
print(f"math_utils module loaded!")Create main.py:
import math_utils
# Use functions from the module
result1 = math_utils.add(10, 5)
result2 = math_utils.multiply(4, 7)
result3 = math_utils.power(2, 8)
print(f"10 + 5 = {result1}")
print(f"4 × 7 = {result2}")
print(f"2^8 = {result3}")Run it:
python main.pyExpected output:
math_utils module loaded!
10 + 5 = 15
4 × 7 = 28
2^8 = 256
Notice: The print() statement in math_utils.py ran when we imported it!
Docstrings are available via help() and pydoc (covered in Lab 1.1):
python>>> import math_utils
math_utils module loaded!
>>> # View module documentation
>>> help(math_utils)
# Shows: "Math utility functions" and all function docs
>>> # View specific function documentation
>>> help(math_utils.add)
# Shows: "Add two numbers"
>>> exit()Or use pydoc from the terminal:
pydoc math_utils
pydoc math_utils.addKey point: Docstrings (triple-quoted strings) are not just comments - they're live documentation accessible via help() and pydoc. Always write them!
Create import_examples.py:
# Method 1: Import entire module
import math_utils
print(math_utils.add(1, 2))
# Method 2: Import specific functions
from math_utils import add, multiply
print(add(3, 4))
print(multiply(5, 6))
# Method 3: Import with alias
import math_utils as mu
print(mu.power(3, 3))
# Method 4: Import everything (not recommended!)
from math_utils import *
print(add(7, 8))Run it:
python import_examples.pyBest practice: Use Method 1 or 2. Avoid import * in production code - it makes it unclear where functions come from.
Create args_demo.py:
# *args - variable positional arguments (becomes tuple)
def add_all(*args):
"""Add any number of arguments"""
return sum(args)
print(f"add_all(1, 2, 3): {add_all(1, 2, 3)}")
print(f"add_all(10, 20, 30, 40): {add_all(10, 20, 30, 40)}")
# **kwargs - variable keyword arguments (becomes dict)
def greet(name, **kwargs):
"""Greet with options"""
greeting = kwargs.get("greeting", "Hello")
punctuation = kwargs.get("punctuation", "!")
return f"{greeting}, {name}{punctuation}"
print(f"greet('Alice'): {greet('Alice')}")
print(f"greet('Bob', greeting='Hi', punctuation='!!!'): {greet('Bob', greeting='Hi', punctuation='!!!')}")
# Unpacking in assignments
numbers = [1, 2, 3, 4, 5]
a, b, *rest = numbers
print(f"\na, b, *rest = {numbers}")
print(f"a={a}, b={b}, rest={rest}")
# Unpacking in function calls
values = [10, 20, 30]
print(f"\nadd_all(*{values}) = {add_all(*values)}")
# Merging dicts with unpacking
defaults = {"host": "localhost", "port": 8080}
overrides = {"port": 443}
config = {**defaults, **overrides}
print(f"\nMerged config: {config}")Run it:
python args_demo.pyKey points:
*argscollects extra positional arguments into a tuple**kwargscollects extra keyword arguments into a dict*listunpacks a list into separate arguments**dictunpacks a dict into keyword argumentsa, *rest = listcaptures remainder in unpacking
A package is a directory containing an __init__.py file. This tells Python "this folder is a package."
cd ~/python-modules
mkdir -p utilsCreate utils/__init__.py:
"""
Utils package - collection of utility modules
"""
print("Utils package initialized!")Create utils/string_utils.py:
"""
String manipulation utilities
"""
def reverse_string(text):
"""Reverse a string"""
return text[::-1]
def capitalize_words(text):
"""Capitalize each word"""
return text.title()
def count_vowels(text):
"""Count vowels in text"""
vowels = 'aeiouAEIOU'
return sum(1 for char in text if char in vowels)Create utils/file_utils.py:
"""
File operation utilities
"""
def count_lines(filename):
"""Count lines in a file"""
try:
with open(filename, 'r') as f:
return len(f.readlines())
except FileNotFoundError:
return 0
def get_file_size(filename):
"""Get file size in bytes"""
import os
try:
return os.path.getsize(filename)
except FileNotFoundError:
return 0# Check the structure
tree utils/
# Or use:
ls -R utils/You should see:
utils/
├── __init__.py
├── string_utils.py
└── file_utils.py
cd ~/python-modulesCreate use_package.py:
# Import modules from package
from utils import string_utils
from utils import file_utils
# Use string utilities
text = "hello world"
print(f"Original: {text}")
print(f"Reversed: {string_utils.reverse_string(text)}")
print(f"Capitalized: {string_utils.capitalize_words(text)}")
print(f"Vowel count: {string_utils.count_vowels(text)}")
# Use file utilities
print(f"\nLines in math_utils.py: {file_utils.count_lines('math_utils.py')}")
print(f"Size: {file_utils.get_file_size('math_utils.py')} bytes")Run it:
python use_package.pyExpected Similar output:
Utils package initialized!
Original: hello world
Reversed: dlrow olleh
Capitalized: Hello World
Vowel count: 3
Lines in math_utils.py: 18
Size: 356 bytes
What happened:
- Python found the
utils/directory - Saw
__init__.py- recognized it as a package - Ran
__init__.py(printed "Utils package initialized!") - Loaded the requested modules
This is one of Python's most common patterns. Let's demystify it!
Create show_name.py:
print(f"The __name__ variable is: {__name__}")Run it directly:
python show_name.pyOutput:
The __name__ variable is: __main__
When run directly, __name__ is always "__main__".
Create import_show_name.py:
import show_name
print("Done importing")Run it:
python import_show_name.pyOutput:
The __name__ variable is: show_name
Done importing
When imported, __name__ is the module name!
The if __name__ == "__main__": pattern lets you:
- Run module directly for testing (the code in the
ifblock executes) - Import as library without running test code (the
ifblock is skipped)
This is the professional way to structure Python files:
- Functions/classes at the top (can be imported)
- Test/demo code at the bottom in
if __name__ == "__main__":block (only runs when executed directly)
Scripts need input! sys.argv is a list containing command-line arguments.
Create show_args.py:
import sys
print(f"Script name: {sys.argv[0]}")
print(f"Number of arguments: {len(sys.argv)}")
print(f"All arguments: {sys.argv}")
# Print each argument
for i, arg in enumerate(sys.argv):
print(f" argv[{i}] = {arg}")Try with different arguments:
python show_args.py
python show_args.py hello world
python show_args.py one two three fourKey points:
sys.argv[0]is always the script name- Arguments start at
sys.argv[1] - All arguments are strings!
Create greet.py:
#!/usr/bin/env python3
import sys
if len(sys.argv) < 2:
print("Usage: python greet.py <name>")
sys.exit(1)
name = sys.argv[1]
print(f"Hello, {name}!")Test it:
python greet.py Alice
python greet.py # Shows usage messageYour task: Create a calculator that works as both a CLI tool and importable module. Avoid using AI to enforce learning these skills.
Requirements:
- Create
calc_cli.pywith:- Module docstring explaining what it does
- Functions that accept any number of arguments using
*args:add(*args)- Add all numberssubtract(*args)- Subtract all numbers from firstmultiply(*args)- Multiply all numbersdivide(*args)- Divide first number by all others
- Function docstrings for each operation
- CLI interface using
if __name__ == "__main__"pattern - Error handling for invalid input and division by zero
- Usage message if arguments are wrong
Usage examples:
# CLI usage
python calc_cli.py add 10 5 → Result: 15.0
python calc_cli.py add 10 5 5 5 → Result: 25.0
python calc_cli.py multiply 2 3 4 → Result: 24.0
python calc_cli.py divide 100 2 5 → Result: 10.0
python calc_cli.py → Shows usage message# Module usage
import calc_cli
result = calc_cli.add(1, 2, 3, 4, 5) # Returns 15
result = calc_cli.multiply(2, 3, 4) # Returns 24Hints:
- Use
*argsto accept variable number of arguments - Check
len(sys.argv) >= 3for minimum argument count (script, operation, at least one number) - Convert string arguments to numbers with
float() - Use
try/exceptfor error handling - For subtract: first - second - third - ...
- For divide: first / second / third / ...
- Test both as CLI and as imported module!
You've completed this lab when you can:
- Create and import Python modules (just
.pyfiles) - Organize modules into packages with
__init__.py - Understand and use
if __name__ == "__main__"pattern - Read command-line arguments with
sys.argv - Create scripts that work as both libraries and CLI tools
- Structure code professionally with separate modules
What you learned:
- Modules - Any
.pyfile is a module you can import - Packages - Directories with
__init__.pyare packages __name__- Changes based on how code is executed (direct vs import)sys.argv- Access command-line arguments as a list of strings- Dual-purpose code - Write libraries that also work as scripts
Why this matters:
- Organize large projects into manageable pieces
- Write reusable code that can be imported anywhere
- Create CLI tools for automation and DevOps tasks
- Professional Python code structure
- Essential for building packages and distributing code
# Import module
import mymodule
from mymodule import function
# Create package (directory structure)
mypackage/
├── __init__.py
├── module1.py
└── module2.py
# if __name__ pattern
if __name__ == "__main__":
# Code here only runs when executed directly
pass
# Command-line arguments
import sys
script_name = sys.argv[0]
first_arg = sys.argv[1]
all_args = sys.argv[1:] # All except script nameCongratulations! You now understand Python modules, packages, and script structure - essential skills for building professional Python applications!