1  Introduction to packaging

Creating a Python package is all about making your code reusable, shareable, and easy to install. Whether you want to publish a library for the world or just organize your own projects better, understanding how packaging works is the first step.

1.1 The __init__.py file

At its simplest, a package is just a folder that contains an __init__.py file.

my_package/
└── __init__.py

The presence of __init__.py tells Python: “this is a package.”

Even if it’s an empty file, it’s important: it allows you to import parts of your code like this:

from my_package import something

Without it, Python treats the directory my_package/ as a regular directory, not something it can import from.

1.2 Package vs Module vs Library

These terms get thrown around a lot. Here’s the quick breakdown:

  • Module: A single .py file (e.g., my_file.py)
  • Package: A directory with an __init__.py, possibly containing multiple modules (e.g., multiple files)
  • Library: A more general programming term and refers to a bundle of code that can be used (source)

Library and package most of the time refer to the same thing. All packages are libraries, the opposite is not true. For the sake of simplicity, it’s ok to consider them “equivalent”, even though we’re are mostly interested in packages in practice.

Now, let’s create the smallest Python package possible.

1.3 Initialize the directory

The very first step is to create a new directory named “my_package”. Inside this directory, create another directory with the same name. The structure should look like this:

my_package/
└── my_package/

1.4 Reusable Python code

When creating a Python package, we want to write a reusable piece of code, not just put in a few scripts that do things. To illustrate:

name = "Joseph"
message = f"Hello {name}"
print(message)
Hello Joseph

This code does something: it prints a message.

def say_hello(name):
   message = f"Hello {name}"
   print(message)

The code above does ‘nothing’. The only thing it does is create a function object that will be stored in memory. I can now call it and it will execute some code. For example:

say_hello("Joseph")
Hello Joseph

1.5 Package structure

Now, let’s create our first Python module (which is just a file ending in .py). We’ll call it my_module.py.

Our file my_module.py will be next to the __init__.py we talked about before:

my_package/
└── my_package/
    ├── __init__.py
    └── my_module.py
def say_hello(name):
   message = f"Hello {name}"
   print(message)
from .my_module import say_hello

__all__ = ["say_hello"]

1.6 Use our package

Now, how do we use our package, from a user point of view?

For this, you’ll need to have uv installed.

Then we’ll need to run a command in our terminal at Desktop/my_package/:

uv init
uv venv
uv pip install -e .

Don’t worry too much about those commands yet.

This command will install our current package in editable mode. This allows us to test our package while making updates.

The next step is to open a new Python file, console or notebook, ideally not in the package directory. I usually like having a sandbox.py file. In this file, we’ll run:

from package_name import say_hello

say_hello("Julia")

Hello Julia

And now we have a fully functional Python package! This is just the beginning, but this is an important fondation to have for what’s coming next.