Packages, __init__.py and if __name__ == "__main__"

Lesson goals
  • Explain what a Package is and build one with __init__.py
  • Import modules and functions from inside a Package
  • Explain what the value of __name__ is when a file is run versus when it is imported
  • Use if __name__ == "__main__": to separate a “library” from a “program”

What will we learn?

  • Package — a folder that contains modules.
  • The file __init__.py and its role.
  • Importing from inside a Package.
  • The special variable __name__, and the pattern if __name__ == "__main__":.

Why do we need this?

When there are 3 modules, they can sit in the same folder. When there are 30, you need folders: everything related to users in one folder, everything related to payments in another. That is how all real projects are built, and also all the libraries you will install (FastAPI is a huge Package with dozens of sub-Packages).

And if __name__ == "__main__": solves the problem from the previous lesson: test code that runs every time someone imports the file.

Explanation from the basics

Package = a folder of modules

A Package is a folder that contains modules (.py files), and usually also a file named __init__.py.

store/                    ← project folder
├── main.py               ← the program you run
└── inventory/            ← a PACKAGE
    ├── __init__.py       ← marks this folder as a package
    ├── products.py       ← module
    └── stock.py          ← module

__init__.py

  • Marks the folder as a Package, explicitly and clearly.
  • Runs automatically the first time something is imported from the Package.
  • Usually empty. You can put convenient imports in it, so that you can write from inventory import add_product instead of from inventory.products import add_product.

(In modern Python you can also import from a folder without __init__.py, but the accepted convention is to include it, so that it is clear this is a package and not just a folder.)

Importing from a Package

The dot separates a folder from a module, exactly like / in a path:

import inventory.products                 # inventory/products.py
from inventory import products            # same module, shorter use
from inventory.products import add_product   # one function

__name__ — “who am I?”

Every module has a built-in variable named __name__. Python sets its value according to how the file was started:

How the file was startedThe value of __name__
Run directly: python prices.py"__main__"
Imported from another file: import prices"prices" (the module’s name)

And therefore:

if __name__ == "__main__":
    # runs ONLY when this file is run directly, NOT when it is imported
Analogy: a chef who is also a supplier

A chef cooks in their own restaurant (running directly), and at the same time sells sauces to shops (importing). When they are in the restaurant, they also serve customers: that is the code inside if __name__ == "__main__". When a shop buys a sauce, it gets only the sauce (the functions), and not a whole meal forced on it.

Syntax

project/
├── main.py
└── package_name/
    ├── __init__.py
    └── module_name.py
# main.py
from package_name.module_name import some_function
from package_name import module_name

# any module that can also be run directly:
def main():
    ...          # what happens when the file is run as a program

if __name__ == "__main__":
    main()

The pattern of a main() function + the condition at the end of the file is the standard in every Python file meant to be run.

First code example

Let’s see __name__ in action. week-2/name_demo/greetings.py:

def hello(name):
    return "Hello, " + name + "!"

print("greetings.py: __name__ is", __name__)

if __name__ == "__main__":
    print("Running greetings.py directly - quick self test:")
    print(hello("Tester"))

week-2/name_demo/app.py:

import greetings

print("app.py: __name__ is", __name__)
print(greetings.hello("Dana"))

Run 1: python greetings.py

greetings.py: __name__ is __main__
Running greetings.py directly - quick self test:
Hello, Tester!

Run 2: python app.py

greetings.py: __name__ is greetings
app.py: __name__ is __main__
Hello, Dana!

Breaking down the code line by line

Run 1 (python greetings.py)Run 2 (python app.py)
__name__ of greetings.py"__main__" (it is the file that was run)"greetings" (it was imported)
The first print in greetingsRunsRuns (code at the top level always runs on import)
The if __name__ == "__main__" blockRuns (self test)Does not run
__name__ of app.py—"__main__"

The conclusion: everything that should run only when the file is run directly (tests, demos, starting the program) goes inside the condition. Definitions of functions and constants stay outside, so that they can be imported.

Another example

A project with a real Package. The structure:

week-2/store/
├── main.py
└── inventory/
    ├── __init__.py
    ├── products.py
    └── stock.py

inventory/__init__.py:

from inventory.products import create_product
from inventory.stock import add_stock, remove_stock

inventory/products.py:

def create_product(name, price):
    if price < 0:
        raise ValueError("price cannot be negative, got " + str(price))
    return {"name": name, "price": price, "quantity": 0}

if __name__ == "__main__":
    print(create_product("Test item", 10))

inventory/stock.py:

def add_stock(product, amount):
    product["quantity"] = product["quantity"] + amount

def remove_stock(product, amount):
    if amount > product["quantity"]:
        raise ValueError("not enough " + product["name"] + " in stock")
    product["quantity"] = product["quantity"] - amount

main.py:

from inventory import create_product, add_stock, remove_stock

def main():
    laptop = create_product("Laptop", 3500)
    add_stock(laptop, 10)
    remove_stock(laptop, 3)
    print(laptop)

    try:
        remove_stock(laptop, 50)
    except ValueError as error:
        print("Error:", error)

if __name__ == "__main__":
    main()

Run (from week-2/store): python main.py

{'name': 'Laptop', 'price': 3500, 'quantity': 7}
Error: not enough Laptop in stock

Breakdown:

  • __init__.py gathers the important functions of the Package. Thanks to it, main.py writes from inventory import create_product, and does not need to know which internal file exactly it sits in. From outside, the Package looks like “one unit”.
  • add_stock(laptop, 10) changes the dict itself, because a dict is Mutable (the data structures and functions topic!). The function does not need to return anything.
  • products.py includes a self test inside if __name__ == "__main__". You can run it alone to test it, and it does not “make a mess” when it is imported.
  • main.py is wrapped in main() + the condition. If someone imports it in the future, nothing will run by itself.
How do you run a file that is inside a Package?

If you try python inventory/products.py, you sometimes get a ModuleNotFoundError, because Python looks for modules from the folder of the file that was run (inventory/), and not from the project folder. The right way to run a module inside a Package is from the project folder, with the -m (module) flag and dots instead of slashes: python -m inventory.products.

Common mistake

Mistake 1: writing "main" instead of "__main__".

if __name__ == "main":
    main()

Nothing runs, and there is no error! The value is "__main__", with two underscores on each side. A silent Logical Error.

Mistake 2: a Circular Import.

a.py imports b, and b.py imports a:

ImportError: cannot import name 'x' from partially initialized module 'a' (most likely due to a circular import)

Two modules that depend on each other are a sign of a bad split. Move the shared part into a third module, which both of them import from.

Mistake 3: putting all the logic inside the if block.

if __name__ == "__main__":
    def calculate(x):
        return x * 2
    print(calculate(5))

Now calculate cannot be imported from another file, because it is defined only when the file is run directly. The functions go outside, and only starting the program goes inside.

Practice

The exercises use only what was taught in this lesson and the lessons before it. Try on your own first, and only then open the solution.

Exercise 1: The structure of a Package

Draw the folder structure of a project store with a file main.py and a Package named inventory, which has two modules: products.py and stock.py. Which file must be inside inventory, and why?

Show solution
store/
├── main.py
└── inventory/
    ├── __init__.py
    ├── products.py
    └── stock.py

__init__.py marks the folder as a Package — a folder of modules that you can import from. It can be empty.

Exercise 2: Importing from a Package

In inventory/products.py there is a function create_product(name, price) that returns a dictionary. Write in main.py three ways to import it and use it.

Show solution
import inventory.products
laptop = inventory.products.create_product("Laptop", 3500)

from inventory import products
mouse = products.create_product("Mouse", 80)

from inventory.products import create_product
desk = create_product("Desk", 900)

The dot in the path (inventory.products) is like / in folders: the module products inside the Package inventory.

Exercise 3: What is name?

In the file greetings.py:

def hello(name):
    return "Hello, " + name + "!"

print("__name__ is", __name__)

What will be printed in each case?

  1. You run python greetings.py.
  2. You run main.py, which contains import greetings.
Show solution
  1. When run directly: __name__ is __main__.
  2. When imported: __name__ is greetings — the module’s name.

Python sets __name__ according to how the file was started. That is the basis of the main guard.

Exercise 4: The main guard

Add to greetings.py (from the previous exercise, with the function hello) a self test that runs only when the file is run directly, and not when it is imported. The test prints quick self test: and then the result of hello("Tester").

Delete the line print("__name__ is", __name__) from the previous exercise.

Expected output (python greetings.py):

quick self test:
Hello, Tester!
Show solution
def hello(name):
    return "Hello, " + name + "!"


if __name__ == "__main__":
    print("quick self test:")
    print(hello("Tester"))
quick self test:
Hello, Tester!

When another file does import greetings, the condition is False — the test does not run, and only the function is available.

Exercise 5: The mathtools package

Build a Package named mathtools with two modules: basic.py with add(a, b), and stats.py with mean(numbers) and maximum(numbers). In main.py, use all three.

add(2, 3)
mean([10, 20, 30])
maximum([10, 20, 30])

Expected output:

add: 5
mean: 20.0
maximum: 30
Show solution
math_project/
├── main.py
└── mathtools/
    ├── __init__.py
    ├── basic.py
    └── stats.py

mathtools/basic.py

def add(a, b):
    return a + b

mathtools/stats.py

def mean(numbers):
    return sum(numbers) / len(numbers)


def maximum(numbers):
    return max(numbers)

main.py

from mathtools.basic import add
from mathtools import stats

print("add:", add(2, 3))
print("mean:", stats.mean([10, 20, 30]))
print("maximum:", stats.maximum([10, 20, 30]))

Summary

  • Package = a folder of modules, with an __init__.py that runs on the first import and can “expose” convenient names.
  • Importing from a Package: from package.module import name. The dot = going into a folder.
  • __name__ is "__main__" when the file was run directly, and the module’s name when it was imported.
  • if __name__ == "__main__": main() — start the program only when run directly. Define functions outside.
  • A module inside a Package is run from the project folder with python -m package.module.

Official documentation