How To Split And Organise Your Source Code Into Multiple Files in Python 3


Split files to organise your code

Splitting your code into separate files is really important as your code becomes larger, when you work with multiple developers, when your code becomes more complex. In python programming, we’ve usually seen all work done in a single .py file. What happens when we keep writing more lines of code? The code becomes impossible to be kept track of for you as well as other developers who might need to contribute to the project. The key factor for developing a large scale application is to keep your code DRY and organized. DRY means Don’t Repeat Yourself and having repetitive code in your project is considered unprofessional. 

We can keep our code organized and dry by splitting it into multiple files or directories. The question arises, is there a way to link the code that we split? Yes, we can split our source code into multiple .py files in python3. Having multiple files of source code is common practice all across the industry. This way of organizing code is known as modules, i.e. a single module is a single .py file. 

In this article, we shall be looking at different ways of splitting our source code, understanding modules, packages, and using import statements. We shall understand these through various examples. 

Working with two files in the same directory

Let’s start with a basic example. We have two files one which contains all the functions that a simple calculator can perform while in our main file we shall call these functions. The current structure of our project file looks like :

#calculator.py

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


def sub(a,b):
 
  return a-b


def multiply(a,b):
 
  return a*b

How can we make these two files communicate with each other? To do that, all we need to do is use the import command and then the filename. As we read above a single filename is called a module. So, import would be in the given format:

#module name is our filename

import module_name

You may have noticed that we did not add the extension “.py” that is because it is assumed that whatever we are importing has the python extension. 

Now, in our main file let’s print the module name to see what happens. According to the output, it shows the complete path of the module. If you notice it says, module ‘calculator’ i.e calculator is a module we have created in python.

#main.py

import calculator
 
print(calculator) 

Output:

To call a function created in the other .py file we shall use :

#module name is our filename
import module_name.function_name

Let’s try calling “add” and “sub” function which is in our calculator.py file. 

#main.py

import calculator


calculator.sub(5,3)

#This doesn't print as we have used return statement

print(calculator.add(2,3))

Output:

Please note we can use as many imports as we want and use them wherever we want across the project.

Working with python files in a new Directory

We learned about modules but now let’s say our project is getting more complex. We need to add another functionality to our project for shopping. For that, we create a new folder and add a new python file in it and it’s functionality.

Our current file structure shall look like this.

#items.py

items = []

def add_item(item):

  items.append(item)

  return print(items)

If we try to import it as import filename we see we are getting an error that no module found. 

#main.py

import items

Output:

That is because we have created a package here. A package is a folder in which we have different modules. Packages are a level up than modules. So, to import a module that is within package we write:

#package_name is the folder name

import package_name.module_name 

The package name is our folder name while the module name is the python file in the folder which is being accessed by a dot.

#main.py

import shopping.items

To call a function within a module we should follow the following syntax:

package_name.module_name.function_name 

Let’s try calling the “add_item” function in our items.py file.

#main.py

import shopping.items



print("Im adding an item!")
shopping.items.add_item("Juice")

shopping.items.add_item("Chips")

Output:

Note: Dot is used between packages to access the module inside, but as you can see there are too many dots when calling a function in our example. We shall see a better way to import later in this article. 

How to create Package with __init__.py

While working with the Package in different languages things can get difficult when you need to export the dependencies along with the module requested by a program but with Python, it is quite simple. Just like the “__init__” function in python, we can create an __init__.py file inside a package that has similar functionality to the __init__.py file used to be a requirement for a package before Python version 3.3. The __init__.py file is the one that is executed first when a program tries to import a module from that package. This file can contain code just like any other Python file, you can define what functions to run before executing any module and what dependencies to export along with the modules.

While creating a python package it is best practice to have an __init__.py file at the root as shown in the current project structure. The interpreter when reading this understands that it is a python package. We shall look at a simple example to understand how __init__.py works. Let’s create a package by the name of car_package and add two python modules with print statements. Our current project structure looks like this:

#Engine.py

def engine():

  print("I am an Engine")

#Tyre.py
def Tyre():
   
  print("I am a tyre")

#main.py
from car_package.Engine import engine
from car_package import Tyre




print("I am init file and I am Executed first")



def Car():
  
  print("I am a car and I can't run without an engine and tyre")



def Engine():
   
  engine()

#main.py

import car_package
from car_package import Tyre



car_package.Car()

car_package.Engine()

Tyre.Tyre()

Output:

If you notice from the above output when we imported the car_package in the main.py file, the __init__.py file was executed first. As we have imported the Engine module inside the __init__.py we do not need to import the Engine module explicitly and we can call it using the function inside the __init__.py file. One more thing to notice is that the __init__.py is only executed once even if we import another module from the same package.

 Various ways of Importing

As we saw in the example above, if we need to call a function within a package it gets too long and unrealistic to call numerous times. For that, we shall use an aliased import. In alias, we use “as” after the function name. Let’s use a famous python package “math” to demonstrate.

import math as mt #mt will be used when we need to access a module or function
print(mt.pi)

Another way to import is by calling the specific function we need. To demonstrate this we shall use the first example calculator.py and main.py. Suppose we need to use only add function in our main file. Import would work like:

from calculator import add 

If we want to import add and sub function we shall use : 

 from calculator import add,sub 

If you want to import all the functions that are given in a python module we use steric (*). This way isn’t preferred by experienced developers as it may affect the time complexity of the program by importing the unnecessary functions. The import command would work like:

 import calculator as * 

Double Underscore or Dunder Methods

This is one of the most important concepts to understand while coding in python language. Every .py file contains these hidden methods in them to make Python more powerful. This special syntax of “ __”  shows that these are the private methods and should not be changed.

We will be discussing some of the important dunder methods related to our topic below. 

What does the __name__ do?

Each python file has a __name__ variable, it usually contains the name of the file in it. Let’s print this __name__ variable in each of our python files. 

#main.pyimport calculator
import shopping.items
 
print(__name__) 

#calculator.py
print(__name__) 

#items.py
print(__name__) 

Output

Python is an interpreted language hence it reads the code line by line during runtime. The main file which we run is given the name by default as “ __main__”, if we use it in a certain manner this can help us to know that the main execution of the program starts from here.

 if __name__ == "__main__":
   print("I'm in main!") 

Note: It is a  good practice to name your python files using snake case i.e do not use spaces while naming your file, use “_” instead of a space. 

What does __file__ do?

__file__  contains the path of the module that we have imported. This is extremely helpful when we are working on large-scale applications and need to find a module’s path so instead of searching for it in different directories, we can simply use __file__ variable.

Suppose in my main.py file I want to know the location of module items.

 #main.pyimport shopping.items
  
 print(shopping.items.__file__)

Output

Best Practice When Splitting Your Code

We have learned the basics of modules, packages when splitting our code. We shall now talk about what not to do when we organize our code in different files.

The main thing we need to be aware of is not using builtin packages or module names for our files and folders. For example, if we have a file called “math.py” and we import math it shall run our math module instead of Python’s builtin package. This happens because the interpreter first searches for the files inside our project folder then looks for folders and files that have been installed in a python default directory.

Subscribe to our newsletter

Get new tips in your inbox automatically. Subscribe to our newsletter!

Charles White

I'm a big believer in the value of learning to code and learning to code is something that everyone should learn these days. Here are a collection of things i've learned over the years..

2 thoughts on “How To Split And Organise Your Source Code Into Multiple Files in Python 3

    1. Thank you for the kind words Joel – let us know if there are other topics you’d like us to cover

      Charles

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Content