Mastering Modular Programming: How to Take Your Python Skills to the Next Level

I recently wrote an article on Python classes, where I said that classes are used as modules in Python, very often. In this article, I'll describe what a module is, how to use modules, and why you should use modules in Python.
At the end of this article, if you've never used a module, you'll switch your mind. You'll change your Programming approach and use modules whenever you can. I'm sure of that. I can understand you: when I discovered modules and their power, I started to use them every time I can.
The only difference between me and you is that I didn't find any complete and valuable resource that treated all you need to know about Python modules: this is why I've created this article. I found fragmented resources here and there. So, thanks to these and thanks to the advice of senior developers, after some days, I had a complete picture of Python modules.
After this article, I'm sure you'll have the picture clear too and don't need anything else to search to understand this topic. So, if you're a beginner in Python, or if you're a "navigate" Pythonista but never developed with modules, then this article is definitely for you.
Why you should modularize your Python code
First of all, let's start our discussion with some reasons why you should use modules in Python. I found 6:
- Better code organization. Modules allow developers to organize their code into reusable units; these can be imported and used in other programs. This means that you shouldn't think anymore about your Python application as a whole program where you write all your code. You'll have a main file where you load and use the modules. We'll see the idea in a practical way in the next paragraphs.
- Code reusability. This is one of the advantages of modules: you create an independent program that can be imported and reused in other programs. Maybe, with slight changes; but often, you can use it as is. This helps us save a lot of time and the amount of code written: writing similar code occurs more often than you think.
- Namespace management. If you've ever coded in any language, you've faced several moments where you have to name variables. Well, as the code grows into your application, you may need to write variables with similar names. Modularity helps us fix names so that we don't have to recreate the wheel each time because we'll use names created in our modules even in our main file.
- Collaboration. When working on a firm or when collaborating with friends on distributed projects (especially using Git), modularity allows us to work independently on modules. This way we can avoid overlapping problems in the same file.
- Easier debugging. If you've debugged a 200 lines application you know how painful it is. Finding an error where you have tens of lines of code can need days. Modularity avoids all this pain because the modules are independent of each other. They represent small programs, so we can debug them easier than a monolith application.
- Performance optimization. Modules can optimize the performance of your machine because we can import only the code we need in the particular application we are developing.
What are modules in Python?
As [1] says: "By module, in Python, we intend packages and libraries, but even any piece of code that is separable from other code and that can work on its own".
So, basically, every Python program is a module because it can work on its own. But this doesn't mean we can import all modules. Or, better: this doesn't mean it always makes sense to import a module into another file.
For example, say you want to create a Python file that checks if three folders exist. If not, it creates them. It can be something like that:
import os
# Define folders name
folders = ["fold_1", "fold_2", "fold_3"]
# Check if folders exist. If not, create folders
for folder in folders:
if not os.path.isdir(folder):
os.makedirs(folder)
This file can be saved as folders.py
and run. It works on its own, and so is a module. But let me ask you a question: does it make sense to import it into another Python file?
Stop reading for a moment and answer this question.
Well, the answer is no, and for a simple reason: it's too specific. I mean: if you ever need to recreate these three folders into another folder, you can move the file into the new folder and run it.
So, when we import modules what we want is generality. This is why we create classes and functions as modules: because they're general. If you want to reuse the above module in other files, you have to generalize it. For example, we can create a function like the following:
import os
def create_folders(fold_1, fold_2, fold_3):
# Define folders name
folders = [fold_1, fold_2, fold_3]
# Check if folders exist. If not, create folders
for folder in folders:
if not os.path.isdir(folder):
os.makedirs(folder)
So, this function can be useful to check if, in a specific folder, there are three general folders. But this time, the names can be any because they are passed as arguments of the function. Then, if they do not exist, they are created. So, for example, we can invoke it like so:
# Invoke the function
create_folders("audio", "image", "documents")
And then our function checks if "audio", "image" and "documents" exist as folders in the directory where your Python file is. If not, it creates them.
This code makes sense to be imported into another Python file because is general: wherever we may need to check for three folders, we can use it. We just need to declare the name of the three folders.
How to use modules in Python
Schematically, modules work like that:

We have a "principal" Python file that usually can be called main.py
. In that Python file, we import two modules.
Maybe you didn't notice, but in the above code, we have imported os
that is a Python module that helps create, manage, and remove directories. This means that, when managing directories, we don't need to create our custom functions (or classes): we can use the os
module and apply it to our specific cases. For example, we have used os.makedirs
that creates folders.
But apart from known modules and packages, how can we use modularity in Python in our everyday programming activities? Well, if you've never used the power of modularity, you'll have to "switch" your mind; but believe me: it's worth it!
If you don't use modules, your Python applications look something like that one:

So, in one file you:
- Create functions and classes.
- Invoke the function and classes created with variables or whatever you may need.
Instead, if you use modules, your Python application becomes something like this:

So, this way, you create two functions and two classes in separate Python files – that are modules. Then, you import them into the main file. At this point, your folder organization should be something like this:
project_name
├── main.py
│ └── packages
│ └── __init__.py
│ └── module_1.py
│ └── module_2.py
│ └── module_3.py
└── └── module_4.py
Of course, you can create how many packages
subfolders you may need.
The important thing is that in each subfolder there must be an __init__.py
file.
Let's see what it is, and how to use it.
A practical example in Python
So, let's make a practical Python example. Let's create a project called my_project
where we have:
- A
main.py
file that is our main file. - A subfolder called
operations
, where we'll have three Python files:__init__.py
,addition.py
,subtraction.py
.
So, this is our structure:
my_project
├── main.py
│ └── operations
│ └── __init__.py
│ └── addition.py
└── └── subtraction.py
We want to create two simple modules that will make the sum and the difference of two integers. If the input is not an integer, the program will return an error.
So, the two modules can be something like this:
def sum(x: int, y: int) -> int:
""" this function sums two integers.
Args:
param 1: integer
param 2: integer
Returns:
an integer that is the sum of the two arguments
"""
if type(x) and type(y) == int:
print(f"the sum of these two numbers is: {x+y}")
else:
print("arguments are not integers!")
if __name__ == "__main__":
sum()
and this:
def diff(x: int, y: int) -> int:
""" this function subtracts two integers.
Args:
param 1: integer
param 2: integer
Returns:
an integer that is the difference of the two arguments
"""
if type(x) and type(y) == int:
print(f"the difference of these two numbers is: {x-y}")
else:
print("arguments are not integers!")
if __name__ == "__main__":
diff()
NOTE:
If you are not familiar with this way of writing Python with type hints,
and if you don't know how ' if __name__ == "__main__"' work, you
have to read my article here and eveything will be clarified.
Now, we'd like to import these two modules into our main file. To do so, we have to write the following in the __init__.py
:
__all__ = ["sum", "diff"]
So, the __init__.py
is a Python file that is necessary to use when we work with modules because, somehow, it tells the main file that in the folder operations
there are some packages to import. So, in the __init__.py
, we have to declare all the functions that are in the packages, as shown above.
Now, the main.py
can be programmed, for example, like so:
from operations.addition import sum
from operations.subtraction import diff
# Sum two numbers
a = sum(1, 2)
# Subtract two numbers
b = diff(3,2)
# Print results
print(a)
print(b)
So, a couple of considerations on the main file:
- As the modules contain just one function each, we could write
from operations.addition import *
. Anyway, is a good practice to import just the functions and the classes we use from a module. - As we can see, the main file is very clean. It just has the imports, the declaration of the variables using the imported methods, and the print of the results.
Final tips
My advice, which is taken from seniors, is to create a class for each module because it's more maintainable. However, this is not a must: it depends on what you are coding.
In the above example, I created two separate modules containing one function each. One alternative would be to create a class called, for example, Operations
and to code the two functions as its method.
This would be helpful because we'd create "similar functions" in just one file and make one import in the main file.
So, don't always take advice as a must: take them, but reason on what you need to create, having in mind the optimization you need.
Conclusions
Now, tell me: how excited are you by this methodology? Well, when I clarified my mind on modules I started applying them to my daily work, and, believe me, it only got better.
So, I hope I have clarified this topic to you; and if I didn't, please: let me know in the comment. I can create other articles as a corollary to that one.
- _Subscribe to my Substack newsletter to get more on Python._
- Join Medium through my referral link: unlock all the content on Medium for 5$/month (with no additional fee).
- Find/contact me here: https://bio.link/federicotrotta
- Found it useful? Buy me a Ko-fi.
More from me:
Python Classes Made Easy: The Definitive Guide to Object-Oriented Programming
Videography:
[1] If name == "main" for Python Developers (video)