Python Packages

In the last lesson on Creating Modules you learned how to build your own module. A module is one .py file you can import into other files. That works great when you have a few of them. But once a project grows, those loose files start piling up in one folder. It gets hard to find anything. A package is how you tidy them up.

🤔 Why Packages?

Picture a project where you keep adding helper files. One for math. One for sending email. One for reading files. One for dates. Pretty soon your folder looks like this.

project/
main.py
math_tools.py
email_tools.py
file_tools.py
date_tools.py
user_tools.py
payment_tools.py

Everything sits at the same level. There is no grouping. So you cannot tell at a glance which file belongs with which. As more files arrive, this only gets worse.

A package fixes this. Here is the plain idea behind it.

  • A package is just a folder that holds related modules together.
  • So you put all the related .py files into one folder and give that folder a name.
  • Now they stay together as a group. Your math helpers live in one folder. Your email helpers live in another.

The pain was a messy flat pile of files. The fix is folders that group them.

🧩 Module vs Package

These two words sound similar. So let us make the difference clear.

  • A module is a single .py file. One file, that is it.
  • A package is a folder that groups many modules together.

So a module is a file, and a package is a folder of those files. Here it is side by side.

Term What it is Example
Module A single .py file math_tools.py
Package A folder that holds modules tools/ with files inside

Simple as that. A file is a module. A folder of files is a package.

📦 A Simple Package Layout

Let us build a small package called mathkit. It will hold two modules. One is for basic math. One is for geometry. Here is the folder layout.

project/
main.py
mathkit/
__init__.py
basic.py
geometry.py

See how mathkit is a folder. Inside it sit the two module files plus a file named __init__.py. So the folder mathkit is the package. The files basic.py and geometry.py are the modules inside it.

Now let us fill in those two modules. First, basic.py holds simple math helpers.

mathkit/basic.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b

Next, geometry.py holds shape helpers.

mathkit/geometry.py
def rectangle_area(width, height):
return width * height
def circle_area(radius):
return 3.14159 * radius * radius

🪧 What Is __init__.py?

You probably noticed that extra file, __init__.py. It looks strange. So let us explain it in plain words.

  • The __init__.py file is what tells Python “this folder is a package, treat it as one”.
  • When Python sees that file in a folder, it knows the folder is meant to be imported.
  • Most of the time you can leave the file completely empty. Just creating it is enough.

You can also use __init__.py to expose things. So if you put an import line inside it, those names become available straight from the package. But you do not have to. An empty file works fine.

Note

In modern Python (3.3 and newer) you can sometimes import a folder even without __init__.py. These are called namespace packages. But adding the empty file is still the clear, safe habit. It removes any guesswork and works everywhere.

So for now, think of __init__.py as a small flag. It marks the folder as a real package.

📥 Importing From a Package

Now the good part. With the package in place, you import from it using the package name and a dot. There are three common ways to do it. Each one ends up with a different name you call.

The first way brings in the whole module path. You then call through the full path.

main.py
import mathkit.basic
print(mathkit.basic.add(4, 6))

Here you call it as mathkit.basic.add. The full path stays in front. That prints 10.

The second way pulls one module out of the package. Then you call through just the module name.

main.py
from mathkit import basic
print(basic.add(4, 6))

Now you call it as basic.add. Shorter, right? That also prints 10.

The third way reaches all the way in and grabs one function. Then you call the function on its own.

main.py
from mathkit.basic import add
print(add(4, 6))

Here you just call add. No module name in front. That prints 10 too.

So the three forms give you three different names to call: the full mathkit.basic.add, the shorter basic.add, or the bare add. Pick the one that reads best for your file.

Here is main.py using two modules together.

main.py
from mathkit import basic
from mathkit import geometry
sum_result = basic.add(4, 6)
area = geometry.rectangle_area(5, 3)
print(f"4 + 6 = {sum_result}")
print(f"Area of 5 by 3 rectangle = {area}")

Run main.py and you get this.

Output

4 + 6 = 10
Area of 5 by 3 rectangle = 15

The dot is the key idea. mathkit.basic means “the basic module that lives inside the mathkit package”. You read it left to right, folder first, then file.

You can also rename a module while importing it. This helps when names are long.

main.py
import mathkit.geometry as geo
print(geo.circle_area(2))

That prints 12.56636. The as geo part gives the module a short nickname for the rest of the file.

🗂️ Subpackages

A package can hold another package inside it. That inner package is called a subpackage. It is just a package within a package.

So say your mathkit grows. You want to split the geometry stuff into its own group. You make a folder inside mathkit and give it its own __init__.py.

project/
main.py
mathkit/
__init__.py
basic.py
shapes/
__init__.py
circle.py
rectangle.py

Here shapes is a subpackage of mathkit. To reach a module deep inside, you just add more dots, one per level.

main.py
from mathkit.shapes import circle
print(circle.area(2))

So mathkit.shapes.circle reads left to right: package mathkit, then subpackage shapes, then module circle. The dots follow the folders.

🧭 Absolute vs Relative Imports

When one module inside a package wants to use another module in the same package, you have two ways to write the import.

  • An absolute import spells out the full path from the package name. Like from mathkit.shapes import circle.
  • A relative import uses dots to mean “from here”. One dot means the current package. So from .shapes import circle means “the shapes next to me”.

Here is the same import written both ways, inside a file that lives in mathkit.

# inside a module in mathkit/
# absolute: full path from the package name
from mathkit.shapes import circle
# relative: the dot means "this package"
from .shapes import circle

Both work. But the absolute one is easier to read because it tells you exactly where the module lives. So prefer absolute imports unless you have a clear reason not to.

🌍 Packages vs Standard Library vs pip Packages

You will hear the word “package” in a few different spots. So let us sort them out.

  • The packages you build yourself are folders of your own modules. They live in your project, like the mathkit folder above.
  • The standard library is the set of packages and modules that ship with Python already. Things like math, os, json, and random. You import them right away, no install needed.
  • Third-party packages are ones other people wrote and shared. You install them with a tool called pip. Things like requests, numpy, and pandas come from here.

So import math uses the standard library. import requests uses a third-party package you installed with pip. from mathkit import basic uses your own package. They all import the same way. The only difference is where the code came from.

🛒 A Worked Example: A Tiny Store Package

Let us build something a bit closer to real life. We will make a small store package. It will hold two modules. One handles products. One handles a shopping cart. Here is the layout.

project/
main.py
store/
__init__.py
products.py
cart.py

First, products.py keeps a small list of products and a helper to find one by name.

store/products.py
products = {
"apple": 30,
"bread": 45,
"milk": 25,
}
def get_price(name):
return products.get(name, 0)

Next, cart.py adds items and works out the total. Notice it imports get_price from the products module using an absolute import.

store/cart.py
from store.products import get_price
cart = []
def add_item(name, quantity):
cart.append((name, quantity))
def total():
amount = 0
for name, quantity in cart:
amount += get_price(name) * quantity
return amount

Now main.py sits next to the store folder and uses both modules together.

main.py
from store import cart
cart.add_item("apple", 2)
cart.add_item("milk", 1)
print(f"Items in cart: {len(cart.cart)}")
print(f"Total to pay: {cart.total()}")

Run main.py and you see this.

Output

Items in cart: 2
Total to pay: 85

So the flow is simple, right?

  • main.py imports the cart module from the store package.
  • add_item drops two products into the cart list.
  • total walks the cart and asks products.get_price for each price, then adds it all up.
  • Two apples at 30 plus one milk at 25 comes to 85.

See how each module does one job, and the package keeps them together. That is the whole point of packages.

⚠️ Common Mistakes

A few small slips can confuse people when they first use packages.

  • Forgetting the package name in the import. You have to go through the folder.
# ❌ Avoid: Python does not know where to find this loose name
from products import get_price
# ✅ Good: go through the package folder first
from store.products import get_price
  • Forgetting __init__.py in older setups. On older Python, a folder without it was not seen as a package at all, so the import failed. Adding the empty file avoids the whole problem.

  • Putting main.py inside the package folder and then getting confused about paths. Keep the script that uses the package outside it, next to the package folder, like in the layouts above.

  • Name clashes. If you name your own module random.py or math.py, it can collide with the standard library and shadow it. So give your modules clear, specific names.

  • Mixing up the module and the function. products is the module (the file). get_price is the function inside it. products.get_price is how you reach the function through the module.

✅ Best Practices

Keep these habits and packages stay pleasant to work with.

  • Give the package a short, clear, lowercase name. mathkit reads better than MathKitFolder.
  • Group only related modules in one package. A package called store should hold store things, not email things.
  • Add the empty __init__.py even when Python might not strictly need it. It makes your intent obvious and works everywhere.
  • Prefer absolute imports inside a package. from store.products import get_price tells you exactly where the code lives.
  • Keep the importing script outside the package folder so the structure stays easy to follow.

🧩 What You’ve Learned

A quick recap of what you can now do.

  • ✅ You know a package is a folder that groups related modules together, while a module is a single .py file.
  • ✅ You can lay out a package folder with module files inside it, including subpackages nested deeper.
  • ✅ You understand that __init__.py marks a folder as a package, can expose things, and is usually empty.
  • ✅ You can import from a package three ways: import mathkit.basic, from mathkit import basic, and from mathkit.basic import add.
  • ✅ You know absolute imports are clearer than relative ones, and where your own packages, the standard library, and pip packages each fit.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is a Python package?

    Why: A package is a folder that holds related modules, while a module is a single .py file.

  2. 2

    What is the main job of the __init__.py file?

    Why: The __init__.py file flags the folder as a package, and it is usually left empty.

  3. 3

    Which import gives you the bare name add, with no module name in front?

    Why: from mathkit.basic import add reaches in and grabs the function, so you call add() directly.

  4. 4

    Where does the third-party package requests come from?

    Why: requests is a third-party package. You install it with pip, then import it the same way as your own packages.

🚀 What’s Next?

Now your code is grouped into neat packages. Next you will learn how to keep each project’s installed packages separate and clean, so two projects never clash.

Virtual Environments

Share & Connect