All Courses
Advanced Python

Python Modules & Packages

Core Definitions

Term Definition
Module Any .py file
Package A directory containing an __init__.py file and Python modules
Main module The module that is directly run (its __name__ equals "__main__")

Modules

Naming Conventions

  • Use snake_case (lowercase with underscores)
  • No spaces, no special characters
  • Avoid names that clash with built-in modules (e.g. don't name your file math.py)

Checking the Module Name

print(__name__)   # prints "__main__" if this file was run directly
                  # prints the module name (e.g. "functions") if it was imported

Importing Modules

Given a file functions.py in the same directory containing:

def foo():
    print("foo")

def bar():
    print("bar")

class Test:
    pass

1. Import the whole module (use prefix to access)

import functions

functions.foo()       # foo
functions.bar()       # bar
t = functions.Test()

2. Import with an alias

import functions as f

f.foo()   # foo
# functions.foo() — no longer works

3. Import specific items (no prefix needed)

from functions import foo, bar, Test

foo()    # foo
bar()    # bar

4. Import everything (use with caution ⚠️)

from functions import *

foo()   # works — but risky

⚠️ Avoid import * unless you truly need everything. It can cause name conflicts and makes code harder to read.


Import Placement

Always put imports at the top of the file. Importing after usage causes a NameError:

f.foo()          # ❌ NameError — f not defined yet
import functions as f

What Happens When You Import a Module

When a module is imported, all the code inside it runs once. This is why: - A print("ran") inside functions.py will print when you import it - Circular imports cause infinite loops (see below)


Circular Imports

A circular import happens when module A imports module B, and module B also imports module A:

modules_and_packages.py  →  imports  →  functions.py
functions.py             →  imports  →  modules_and_packages.py

This creates an infinite loop because importing triggers all code to run. Avoid this by having one main module that imports from others — not modules importing each other.


if __name__ == "__main__"

Use this guard to run code only when the file is executed directly, not when it's imported:

def foo():
    print("foo")

if __name__ == "__main__":
    print("Running directly!")   # only runs if this file is the main module
    foo()

When imported, __name__ is the module's filename (e.g. "functions"), so the block is skipped.


Packages

Creating a Package

A directory becomes a package by adding an __init__.py file inside it:

my_project/
│
├── main_module.py
└── code/               ← package (has __init__.py)
    ├── __init__.py
    ├── a.py
    └── b.py

Without __init__.py → just a folder. With it → a Python package.

__init__.py

  • Runs automatically whenever the package is imported
  • Can be left empty, or used to pre-import things from the package
# code/__init__.py
from code.a import a   # now accessible as code.a directly
from code.b import b

Importing from Packages

Absolute Imports (recommended ✅)

Specify the full path from the top-level package:

import code.a                  # import the module
code.a.a()                     # call function a from module a

from code.a import a           # import the function directly
a()

from code import a, b          # works if __init__.py imports them

Nested packages

main/
├── a/
│   └── p1/
│       └── run_code.py
└── b/
    └── p2/
        └── print_something.py
import main.a.p1.run_code
from main.b.p2 import print_something

Relative Imports (advanced, use sparingly ⚠️)

Relative imports use dots to indicate location relative to the current file:

from . import b          # import b from the current package
from .. import b         # go one level up, then import b
from ...b.p2 import x    # go two levels up, into b/p2, import x

Rules: - Must use from ... import syntax (can't do import ...module) - Only works when the file is part of an imported package — not when run directly - Generally considered less readable than absolute imports — prefer absolute imports


Summary: Key Rules

Rule Detail
Module = .py file Any Python file is a module
Package = directory + __init__.py The init file makes it a package
Import runs code once All code in a module runs the first time it's imported
Avoid circular imports Don't have two modules import each other
Use if __name__ == "__main__" To protect code that should only run when executed directly
Prefer absolute imports Clearer, less error-prone than relative imports
Name modules in snake_case No spaces, no special characters, no clashes with built-ins