Typically, we break code in large projects into modules — separate files that store Python code. Sometimes, even this separation isn't enough. You may want to group your modules by meaning or purpose and assign a separate set for use in other projects.
In such cases, packages, which combine modules, can help. There are also sub-packages within packages in large projects, but we won't go into that. In this lesson, we'll look at how to create packages and add modules to them. We will also talk about how to import packages into a project.
How to create packages
In structure, a package is a directory with module files, named using snake_case and containing, among other things, a specific module named __init__.py
. The presence of this file tells the Python interpreter that it should treat the directory specifically as a package.
Let's look at an example of a basic package. Create a package from the package
directory and the __init__.py
module inside that directory:
package/
└── __init__.py
We add the following code to the __init__.py
file:
# file __init__.py
NAME = 'super_package'
Now we have a small but complete package. We can import it in the same way as a module:
import package
print(package.NAME)
We don't need to import __init__.py
separately. The first time you access the package, Python imports the __init__.py
module. It happens automatically because a directory without an init file won't be considered a package.
How to add modules to packages
With a simple package, everything is clear. We can use it as a module. Now let's move on to grouping. Let's add two more modules to the package:
package/
├── constants.py
├── functions.py
└── __init__.py
Let us look at the contents of the constants.py
module:
# file constants.py
PERSON = 'Alice'
And the contents of the functions.py
module:
# file functions.py
def greet(who):
print('Hello, ' + who + '!')
Now there isn't only __init__.py
in the package, but also two more modules, which you can import.
How to import packages
In the chapter on modules, we mentioned two options:
- Qualified import (also called whole module import)
- Importing individual definitions
Let's apply both import methods — but this time not to modules, but to packages.
Qualified importing helps you write readable code. By reading the function call line, another developer will immediately understand where the function and its argument came from. In this example, the qualified import looks like this:
import package.functions
import package.constants
package.functions.greet(package.constants.PERSON) # => Hello, Alice!
Importing individual definitions is easier because you don't have to write the package and module name each time. On the other hand, it's harder for other developers to read the code. To find out where the function and constant came from, they look in the import block.
Let's try to import the individual definitions, meaning the function itself and the argument:
from package.functions import greet
from package.constants import PERSON
greet(PERSON) # => Hello, Alice!
In addition, imports come in two types:
- Absolute
- Relative
Before, we looked at importing modules without separating the two types. But it is critical for understanding packages, so we'll discuss the details here.
Absolute imports
Absolute import requires you to write the full path to the module, including all packages and sub-packages.
Full paths make the code easy to read and unambiguous, so everyone knows what we imported and where it came from. We have used absolute importing in all the examples above to make it easier for you to read the code.
Relative imports
Relative imports look like this:
from . import module
from .module import function
from .subpackage.module import CONSTANT
Relative importing uses a dot, which means importing the module from the current directory.
For example, we're working in the main.py
file and want to import .module
. We know that main.py
and .module
are stored in the same directory. It means you don't have to write an absolute path to .module
, but use . import
. At this point, Python will automatically determine what we want to import and from where.
Relative import helps you write faster, but it makes the code too messy and less readable. That is why there is some general advice for beginners in the Python development community: try to use absolute import, even in the most obvious cases.