What is a Python package?
A Python package is simply an organized collection of python modules. A python module is simply a single python file.
Why would I want to create a package using __init__.py
?
Creating a package with __init__.py
is all about making it easier to develop larger Python projects.
It provides a mechanism for you to group separate python scripts into a single importable module.
Let’s run through some examples
The best way to understand why you would use __init__.py
and to learn how to use it to create a package is to run through some quick examples! The best way to learn is by doing!
The code in this tutorial should work for Python 2 or 3. Just remember, if you are using 2 then you will need to use the from __future__ import print_function
functionality.
Say we have three modules we have created:
someFolder
|-- stringLength.py
|-- stringToUpper.py
`-- stringToLower.py
Remember a module is just another name for any single python file
For our example, the content of these files is the following:
# stringLength.py
def stringLength(inStr):
return len(inStr)
# stringToUpper.py
def stringToUpper(inStr):
return inStr.upper()
# stringToLower.py
def stringToLower(inStr):
return inStr.lower()
Obviously, these functions are useless, but it helps to serve as a model for the basic concept that we have some python modules that we have already written that are somehow related.
So, without creating a package and using __init__.py
, how do we use the functions in these files?
Well, we can only import these files if they are in the current directory that whatever script we are running is running from.
Well, we can use these files in a new Python script but with one key caveat:
- The files must be in the same directory as the script we are trying to use them in.
To illustrate that, let’s create a file called example1.py
that leverages our modules:
# example1.py
import stringLength
import stringToLower
import stringToUpper
some_string = "Hello, Universe!"
print(stringLength.stringLength(some_string))
print(stringToLower.stringToLower(some_string))
print(stringToUpper.stringToUpper(some_string))
Adding a blank __init__.py
What if we wanted to seperate these scripts into a folder in order to keep them more organized?
Well, that is where the __init__.py
file comes into play.
First, lets move our scripts into a new subfolder and call it: string_func
. Then create an empty file in that folder called __init__.py
Here is our new file/folder structure:
someFolder
|-- string_func
| |-- __init__.py
| |-- stringToUpper.py
| |-- stringToLower.py
| `-- strengthLength.py
`-- example1.py
So, now let’s test out exactly what __init__.py
allows us to do:
Let’s make a new example2.py
file.
# example2.py
import string_func.stringLength
import string_func.stringToLower
import string_func.stringToUpper
some_string = "Hello, Universe!"
print(string_func.stringLength.stringLength(some_string))
print(string_func.stringToLower.stringToLower(some_string))
print(string_func.stringToUpper.stringToUpper(some_string))
So, now we can access our string functions in this manner. This is great, because they are all in a seperate folder, but the syntax is definitely not very succinct. Let’s see if we can clean things up a bit by editing our __init__.py
file.
Adding imports to init.py
Open your __init__.py
file and make the following changes:
# __init__.py
from .stringLength import stringLength
from .stringToLower import stringToLower
from .stringToUpper import stringToUpper
Note that the .
before the module name is neccessary as of Python 3 since it is more strict regarding relative imports: https://stackoverflow.com/questions/12172791/changes-in-import-statement-python3?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
And so with that in our __init__.py
we can now shorten our code to:
# example3.py
import string_func
some_string = "Hello, Universe!"
print(string_func.stringLength(some_string))
print(string_func.stringToLower(some_string))
print(string_func.stringToUpper(some_string))
Now the syntax is a lot shorter and you can see that string_func
is behaving like its own module.
So, that is basically what __init__.py
does! It allows you to treat a directory as if it was a python module. Then you can further define imports inside your __init__.py
file to make imports more succinct, or you can just leave the file blank.
Debugging import
Issues
There are basically 3 tips I have for debugging import issues:
- Use the interactive interpreter (The REPL) to import the modules and see if you are getting what you expect.
- Start your script with
python -v -m my_scriptname.py
and then check the output to see exactly where your modules are getting imported from. - Use Pycharm. Pycharm’s fantastic introspection abilities mean that you will immeadiately know whether or not your module is being properly imported as it will indicate an error if not. It will sometimes also suggest the proper correction. The community edition is free and if you’re a student you can get a free subscription to ALL of their products!
For more information about python modules and packages you can see check the python documentation on it.
You can also check out this great Talk Python To Me podcast with David Beazley where he discusses the subject, as well as David’s talk on the same subject.