Importing Modules
# import module - access with module.name
import math
print(math.pi) # 3.141592653589793
print(math.sqrt(16)) # 4.0
print(math.ceil(4.3)) # 5
# import with alias
import datetime as dt
import collections as col
today = dt.date.today()
# from module import name - brings name into current scope
from math import pi, sqrt, ceil
print(pi) # 3.141592653589793
print(sqrt(9)) # 3.0
# from module import name as alias
from math import sqrt as square_root
print(square_root(25)) # 5.0
# Import multiple from one module
from os.path import join, exists, dirname, basename
# from module import * - avoid in production code
# from math import * # imports everything - can cause name collisions
# Conditional import - handle missing optional dependency
try:
import ujson as json # fast JSON library (optional)
except ImportError:
import json # fall back to standard library
data = json.dumps({"key": "value"})
# Lazy import (defer import until needed)
def process_image(path):
from PIL import Image # only imported when function is called
img = Image.open(path)
return img
# Inspect what's in a module
import os
print(dir(os)) # list of all names in the module
print(os.__file__) # path to the module file
print(os.__doc__[:80]) # first 80 chars of module docstring
Creating Modules
Any Python file is a module. Name it with lowercase and underscores.
# mymath.py - a simple module
"""Utility math functions."""
PI = 3.14159265
def circle_area(radius):
"""Return the area of a circle."""
return PI * radius ** 2
def factorial(n):
"""Return n! recursively."""
if n <= 1:
return 1
return n * factorial(n - 1)
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def magnitude(self):
return (self.x**2 + self.y**2) ** 0.5
def __repr__(self):
return f"Vector2D({self.x}, {self.y})"
# main.py - using the module
import mymath
print(mymath.PI) # 3.14159265
print(mymath.circle_area(5)) # 78.53981625
print(mymath.factorial(5)) # 120
v = mymath.Vector2D(3, 4)
print(v.magnitude()) # 5.0
# Or import specific names
from mymath import circle_area, Vector2D
print(circle_area(3)) # 28.27...
v2 = Vector2D(1, 1)
print(v2.magnitude()) # 1.414...
Packages
A package is a directory containing Python modules and an __init__.py file.
mypackage/
__init__.py # makes it a package; can be empty or have exports
utils.py
math/
__init__.py
geometry.py
statistics.py
io/
__init__.py
readers.py
writers.py
# mypackage/__init__.py
"""MyPackage - a collection of utility functions."""
# Re-export commonly used items at the package level
from .utils import helper_function
from .math.geometry import circle_area, Rectangle
__version__ = "1.0.0"
__author__ = "Alice"
# Absolute imports (recommended)
import mypackage
from mypackage import circle_area # re-exported from __init__
from mypackage.math.geometry import Rectangle
from mypackage.io import readers
# Relative imports (inside a package)
# Inside mypackage/math/geometry.py:
# from ..utils import helper_function # go up one level, then to utils
# from . import statistics # same package
# from .statistics import mean, stdev # specific names from sibling module
# Relative imports only work inside a package - not in top-level scripts
__name__ == "__main__"
# script.py
def greet(name):
return f"Hello, {name}!"
def main():
import sys
name = sys.argv[1] if len(sys.argv) > 1 else "World"
print(greet(name))
# When run directly: python script.py Alice
# __name__ is "__main__" -> main() is called
# When imported: from script import greet
# __name__ is "script" -> main() is NOT called
if __name__ == "__main__":
main()
# Check from another file
# import script
# print(script.__name__) # "script" (not "__main__")
# print(script.greet("Bob")) # Hello, Bob!
# Testing example - run tests only when executed directly
if __name__ == "__main__":
# Quick smoke tests
assert greet("Alice") == "Hello, Alice!"
assert greet("World") == "Hello, World!"
print("All tests passed")
__all__ and Exports
# __all__ controls what 'from module import *' exports
# and signals the public API of the module
__all__ = ["PublicClass", "public_function"] # explicit public API
class PublicClass:
"""This is part of the public API."""
pass
def public_function():
"""This is part of the public API."""
pass
def _private_function(): # leading _ means private by convention
"""Not exported by * and signals internal use."""
pass
class _InternalHelper: # won't be exported
pass
# Without __all__, 'from module import *' imports all names
# that don't start with _
# Checking what a module exports
import mymodule
print(mymodule.__all__) # ['PublicClass', 'public_function']
# Good practice: define __all__ in every module/package
# It serves as documentation and prevents accidental exports
Standard Library Modules
# A tour of the most useful standard library modules
# os - operating system interface
import os
print(os.getcwd()) # current directory
print(os.listdir(".")) # directory contents
os.makedirs("path/to/dir", exist_ok=True) # create dirs
print(os.environ.get("HOME", "/")) # environment variables
# pathlib - modern file path handling (preferred over os.path)
from pathlib import Path
p = Path("/home/alice/projects")
print(p.name) # projects
print(p.parent) # /home/alice
print(p / "file.txt") # /home/alice/projects/file.txt
print(p.exists()) # True/False
# sys - interpreter internals
import sys
print(sys.version) # Python version
print(sys.argv) # command-line arguments
print(sys.path[:3]) # import search paths
# datetime - dates and times
from datetime import datetime, date, timedelta
now = datetime.now()
today = date.today()
tomorrow = today + timedelta(days=1)
print(now.strftime("%Y-%m-%d %H:%M")) # 2024-06-15 14:30
# collections - specialized data structures
from collections import Counter, defaultdict, deque, OrderedDict, namedtuple
# (See Dictionaries tutorial for Counter and defaultdict)
dq = deque([1, 2, 3], maxlen=5) # O(1) append/pop from both ends
dq.appendleft(0)
dq.append(4)
print(dq) # deque([0, 1, 2, 3, 4], maxlen=5)
# itertools - combinatoric and lazy iterators
import itertools
print(list(itertools.chain([1, 2], [3, 4], [5]))) # [1, 2, 3, 4, 5]
print(list(itertools.islice(range(1000), 5))) # [0, 1, 2, 3, 4]
# functools - higher-order functions
from functools import lru_cache, partial, reduce
double = partial(lambda x, y: x * y, 2) # bind first arg to 2
print(double(5)) # 10
# re - regular expressions
import re
email_pattern = r"[\w.+-]+@[\w-]+\.[\w.]+"
emails = re.findall(email_pattern, "Contact alice@example.com or bob@test.org")
print(emails) # ['alice@example.com', 'bob@test.org']
# json - JSON encoding/decoding (see JSON tutorial)
import json
data = json.dumps({"name": "Alice", "age": 30})
obj = json.loads(data)
# random - random number generation
import random
print(random.randint(1, 6)) # dice roll
print(random.choice(["a", "b"])) # random element
random.shuffle([1, 2, 3]) # in-place shuffle
| Module | Key Use |
|---|---|
os | OS operations, environment variables, file system |
pathlib | Modern file path handling (prefer over os.path) |
sys | Interpreter info, argv, path, exit |
datetime | Dates, times, timedelta, formatting |
collections | Counter, defaultdict, deque, namedtuple |
itertools | chain, combinations, permutations, groupby |
functools | lru_cache, partial, reduce, wraps |
re | Regular expressions |
json | JSON encode/decode |
math | sqrt, ceil, floor, log, trig, pi |
random | Random numbers, sampling, shuffling |
typing | Type hints - Optional, Union, List, Dict |