Python Basics #20 — Modules and Packages Vol. 2
In the previous lesson we covered modules; in this lesson we’ll cover packages. If you haven’t read the previous lesson yet, please start there:
Python Basics #20 — Modules and Packages Vol. 1
If a module is a collection of related functions in one file, a package is a collection of related modules in one folder. In Python, packages use dot notation to organize modules hierarchically — like folders on a computer. Windows uses backslashes (\) and Linux uses slashes (/) for folder paths; Python uses dots (.) for package paths.
The most important reason to use packages is distribution. When sharing modules with others, putting them inside a nicely-named folder is much better than handing over a pile of loose Python files. Packages also prevent name collisions between modules with the same name in different packages. Just as you access a function inside a module with module.function, you access a module inside a package with package.module. User-installed packages are placed in the site-packages folder under Lib, in a folder named after the package.
Let’s install the numpy package with pip and see the folder appear. Inside the top-level package folder you can create sub-folders that group modules by category — these are called sub-packages.
Now let’s build a simple package. To make a game-like program (similar to the previous lesson), I created an attack.py module containing attack-related functions:
def arrow():
print('[Magic: Arrow] [Damage: 30]')
def fireball():
print('[Magic: Fireball] [Damage: 50]')
def goblin():
print('[Unit: Goblin] [Damage: 20]')
def giant():
print('[Unit: Giant] [Damage: 60]')First, create a game package and place the attack module inside it. When you create a package in PyCharm, an __init__.py file is created alongside the package folder. The Python interpreter looks for __init__.py to distinguish a regular folder from a package folder. Since Python 3.3, you can technically create a package without __init__.py, but __init__.py is heavily used for package initialization, so most packages still include it. As the game’s attack features grow, the module gets larger — split it into separate modules by attack type, then collect those modules into a sub-package.
Split the attack functions into magic and unit modules:
def arrow():
print('[Magic: Arrow] [Damage: 30]')
def fireball():
print('[Magic: Fireball] [Damage: 50]')def goblin():
print('[Unit: Goblin] [Damage: 20]')
def giant():
print('[Unit: Giant] [Damage: 60]')Most games have not just attack but also defense functions. Let’s add a defense sub-package too. (For brevity, I won’t fill in the defense modules.)
Now let’s see how a user would import and use the game package from their own program. Make a Python entry-point file and import the game package. To import a module inside a sub-package, use package.subpackage.module:
import game.attack.magic
game.attack.magic.arrow()[Magic: Arrow] [Damage: 30]But typing the whole package path every time is tedious. Use as or from to shorten it:
# using as
import game.attack.magic as magic
magic.arrow()# using from
from game.attack import magic
magic.arrow()When packages get more complex, the developer usually makes things easier for users by auto-importing modules in __init__.py. When you import a package, its __init__.py runs automatically. Add a print to game/__init__.py and run the program — you’ll see the print statement run when the game package is imported.
If you put import statements in __init__.py, the modules are auto-loaded into the game namespace, and the user can access magic and unit through the game package without going through the attack sub-package:
from .attack import magic, unitfrom game import magic, unit
magic.arrow()
unit.giant()[Magic: Arrow] [Damage: 30]
[Unit: Giant] [Damage: 60]You can even let users access functions directly by name:
from .attack.magic import *
from .attack.unit import *from game import *
arrow()
giant()[Magic: Arrow] [Damage: 30]
[Unit: Giant] [Damage: 60]A package built like this is then packaged for distribution using libraries like setuptools. setuptools is a bit advanced for the basics course, so we’ll cover it in detail in the advanced course. What we’ve covered here is enough for the basics.
Thanks so much for reading the entire Python Basics series! 🚀