A Quick Guide to Understanding Python’s Import Statement
By: Richard Russell
Tags: Programming, Computer Science, Python, Software Development

Heracles battling the Nemean Lion. (Source)

In today’s article, I will be going over Python’s import statements. I realized that import is a really important skill to master, especially when it comes to structuring one’s Python project.

While making Automated Stoic, I wanted to use a few of the source code from Memento Mori. However, since I did not know how to properly use the import statement, I had to manually rewrite the code from one file into the other.

And so, today I will be going in deep about import statement, what it does, and how one could utilize it to its maximum.

What is an Import Statement?

Before we get into the specific details of how one could utilize the import statement, let us take a deep dive to understand the fundamental definition behind this method.

According to Python’s official documentation, an import statement can be defined as the following:

The Python code in one module gains access to the code in another module by the process of importing it. The import statement is the most common way of invoking the import machinery, but it is not the only way. Functions such as importlib.import_module() and built-in __import__() can also be used to invoke the import machinery.

In short, the definition of an import statement is that it will transfer all of the code from one file to another Python file.

For example, let’s suppose that you have two Python files: first.py and second.py . Now, assume that the code in first.py is already written alongside its variables and functions. If you want to use these variables and functions in second.py , you can just import the first.py file and immediately gain access to the variables and functions.

Before we continue, what exactly is a module? In general programming, a module is a piece of software that has a specific functionality. For example, when building a tic-tac-toe game, a module will be responsible for the logic behind the game while another logic will be responsible for drawing the board. Each module is a different file, which can be edited separately.

In this case, a module in terms of Python programming is simply a Python file with a .py extension where a set of functions, variables, or classes are defined and implemented. The name of the module will then be the name of the file. In the case of the example above, the Python files first.py and second.py are modules with the name first and second respectively.

Furthermore, it is also important to note that whenever a developer is accessing modules using the import statement, that module is essentially executed. This allows the developer to make use of the scope of definitions within the imported module.

To import the module, Python needs a fully qualified name of the module being imported. This may come from various arguments such as the import statement or from parameters to the importlib.import_module() or __import__() functions. If any of the imports fail, then a ModuleNotFoundError is raised.

Ways to Import a Module

Since we have already gained a good grasp of what an import statement is, let us see three different ways in which we can import a module!

import

The first way to import a module is by using the basic import statement. Usually, importing is done in the header section of the code, before writing any specific functions.

When we import a module, we are making it available to us in our current program as a separate namespace. This means that we will have to refer to the functions of the imported module in dot notation, such as [module].[function].

Here is an example:

import random

print(random.randint(1,25))

In the code above, we imported the random module, which is a built-in Python module. From that module, we would like to utilize their random integer function, randint() . Since the random module is available to us as a separate namespace, we would have to use dot-notation to refer to randint() , hence random.randint() .

from [x] import [y]

Now, if we want to import a specific function, we can use from ... import ... . This method of importing will also allow us to call the function without using the dot-notation. Here is an example:

from random import randint

print(randint(1,25))

Since we are using the from ... import ... notation, our aim is to use a specific function within the module. In this case, we are accessing the randint function within the random module. Doing so will allow us to call randint() without having to use the dot-notation.

In some programs, you might see people using from module import * . This statement will take every scope of definition within the local namespace. However, this is discouraged as it could potentially replace your locally defined variables or functions.

Before we continue to the last method, I would like to give a brief explanation of the differences between import x and from x import * . When a programmer uses import x , the programmer is essentially creating a reference to that module in the current namespace of x . This is why you need to define a module path to access a particular attribute or method from that module (such as x.name or x.function() )

On the other hand, if the programmer uses from x import * , then the programmer is creating a reference to all public objects in x within the local namespace of the current module. This way, the programmer won’t have to define a module path such as x.name , but can simply refer to it as name .

However, the downside of using from x import * is that if you already have name defined in your local module, then it will be replaced by the name that came from the x module.

import [x] as [z]

This method is called aliasing methods. In the similar case above, you can create an alias of a method if the same name is present within your local project.

For example, if you already have a variable named x inside your local project, you can import your module x by giving it an alias.

import x as x_module print(x_module.name)

Within the program, we can now access x.name as x_module.name .

Styling Import Statements

According to the official style guide for Python (PEP 8), there are a few key points when it comes to styling import statements:

Imports should always be written at the top of the file after any comments and docstrings. Imports should be divided according to what is imported. There are generally three groups: standard library imports (built-in modules), related third party imports (modules that are installed but do not belong to the current application), and local application imports (modules that belong to the current application) Each group of imports should be divided by a blank space Write your imports alphabetically

Here is an example:

"""Illustration of a good import statement styling. Note that imports should come after docstring""" #Standard Library imports

import datetime

import os #Third pary imports

from flask import Flask

from flask_restful import Api

from flask_sqlalchemy import SQLAlchemy #Local application imports

from local_module import local_class

from local_package import local_function

As you can see above, the code starts of with the docstring. A docstring is used to document a specific segment of the code. Multi-line docstrings consist of a summary line, followed by a blank line, followed by a more elaborate description of the module.

Absolute vs Relative Imports

After understanding the different methods to import a module, let us look deeper into how to import modules from a tree of a directory. In short, there are two ways: absolute imports and relative imports.

Absolute Imports

An absolute import specifies the module being import using its full path from the project root’s folder.

Let’s say we have the following directory structure:

└── project

├── package1

│ ├── module1.py

│ └── module2.py

└── package2

├── __init__.py

├── module3.py

├── module4.py

└── subpackage1

└── module5.py

We have the root directory called project , which contains two sub-directories, package1 and package2. Here, it is good to remember that a package is basically a directory that holds one or more modules.

Here, package1 has modules module1 and module2 , while package2 has an __init__.py file, two modules, and another subpackage which contains module5 .

For the sake of this example, let us assume the following:

package1/module2.py contains a function called function1 package2/__init__.py contains a class called class1 package2/subpackage1/module5.py contains a function called function2 .

With these assumptions, we can then write the following absolute imports:

from package1.module2 import function1

from package1 import module1

from package2 import class1

from package2.subpackage1.module5 import function2

As you can see from above, using absolute import is similar to calling the path of the file, except that we are using a dot instead of a slash.

Absolute imports are preferred and highly recommended by PEP 8, as it clearly shows the path of the function/module. However, absolute imports might get a little too long, such as the following:

from package1.package2.package3.package4.package5 import function6

In this case, we can use relative import as an alternative.

Relative Imports

Unlike absolute imports that imports from the root of the project directory, relative imports is a method that imports relative to the current file the import statement is called.

There are two types of relative imports: implicit and explicit. However, since implicit relative imports have been deprecated in Python3, we will not be covering it.

One way to call a relative import is by using dot notation to specify the location. A single dot means that the module or package referenced is in the same hierarchy as the current project, while two dots mean that the referred package is one directory above. Three dots means two directories, and so on. This is similar to how you would use cd .. to go one directory up.

Let’s assume the same directory as the structure above.

In this case, if you want to import function1 which is located in module2 inside module1 (both are in the same package), then you would have to write the following code:

#package1/module1.py

from .module2 import function1`

From the example above, you start the import statement with a dot-notation, signifying the use of relative imports. Since it is only one dot, the computer will recognize that module2 is located in the same directory as module1 . After finding module2 , it will then import function1 to module1 .

Take a look at another example:

#package2/module3.py from . import class1

from .subpackage1.module5 import function2

Recall that the location of class1 is in __init__.py , while function2 is located in package2/subpackage1/module5.py . Since module3 is located in the same directory as __init__.py and subpackage1.module5 , we can easily import class1 and function2 by using relative import statements with one dot notation — signifying that they are located in the same directory.

Aside from shortening long absolute imports, relative imports are not encouraged as they could cause confusion on a shared project.

Summary

To finish this article, let us summarize and review what we have learned today!

Import statements will let a module gain access to another module’s defined scopes (variables, functions, classes)

Modules are files with a .py extension; each module should serve a specific function

extension; each module should serve a specific function There are three ways to import a statement:

import x will refer to the module in its respective namespace. This means we will have to use dot-notation to call any of its variables/functions. from x import y will only import y from module x . This way, you won’t have to use dot-notation. (Bonus: from x import * will refer to the module in the current namespace while also replacing any names that are the same) import x as y will import module x which can be called by referring to it as y

The four rules of making import statements according to PEP 8:

Import statements should be written after the docstrings and comments Import statements should be separated according to what is imported: built-in modules, installed modules that are not part of the application, modules that are part of the application. There should be a space in between each of the import categories Your imports should be in alphabetical order

There are two distinct ways of calling an import:

Absolute import: uses dot-notation to call an import file relative to the root of the directory Relative import: Import modules relative to the current file. It also uses dot-notation but starts the import with number of dots signifying the relative position.

Conclusion

The reason why I wanted to study import statements is that I wanted to use Memento Mori’s code for Automated Stoic without having to copy and paste the whole codebase.

To put this into practice, I will be going through the changes I made to utilize import in my past project.

Remember: stay consistent.

Sources: