Day 21
Modules and Packages
1
Agenda
In this module, you will learn about:
Theory:
What is a package
Practicals:
Create module file
Some steps to create packages.
What is a package?
• Writing your own modules doesn't differ much from writing ordinary scripts.
• There are some specific aspects you must be aware of, but it definitely isn't rocket science.
You'll see this soon enough.
• Let's summarize some important issues:
• a module is a kind of container filled with functions - you can pack as many functions as you
want into one module and distribute it across the world;
• of course, it's generally a good idea not to mix functions with different application areas within
one module (just like in a library - nobody expects scientific works to be put among comic
books), so group your functions carefully and name the module containing them in a clear and
intuitive way (e.g., don't give the name arcade_games to a module containing functions
intended to partition and format hard disks)
• making many modules may cause a little mess - sooner or later you'll want to group your
modules exactly in the same way as you've previously grouped functions - is there a more
general container than a module?
• yes, there is - it's a package; in the world of modules, a package plays a similar role to a
folder/directory in the world of files.
• Your first module: step 1
• Let's start by creating a Python file named module.py which will eventually become our module
containing functions for summing and product calculations:
• Creating a module.py file
• You will need two files to repeat these experiments. The first of them will be the module
itself. It's empty now. Don't worry, you're going to fill it with actual code soon.
• We've named the file module.py. Not very creative, but simple and clear.
• Your first module: step 2
• The second file contains the code using the new module. Its name is main.py. Its content is very
brief so far:
• Note: both files have to be located in the same folder. We strongly encourage you to
create an empty, new folder for both files. Some things will be easier then.
• Launch IDLE (or any other IDE you prefer) and run the main.py file. What do you see?
• You should see nothing. This means that Python has successfully imported the contents of the
module.py file.
• It doesn't matter that the module is empty for now. The very first step has been done, but before
you take the next step, we want you to take a look into the folder in which both files exist.
• Do you notice something interesting?
• A new subfolder has appeared - can you see it? Its name is __pycache__. Take a look inside. What
do you see?
• There is a file named (more or less) module.cpython-xy.pyc where x and y are digits derived from
your version of Python (e.g., they will be 3 and 8 if you use Python 3.8).
• The name of the file is the same as your module's name (module here). The part after the first dot says
which Python implementation has created the file (CPython here) and its version number. The last part
(pyc) comes from the words Python and compiled.
• You can look inside the file - the content is completely unreadable to humans. It has to be like that, as
the file is intended for Python's use only.
• When Python imports a module for the first time, it translates its contents into a somewhat compiled
shape.
• The file doesn't contain machine code - it's internal Python semi-compiled code, ready to be executed
by Python's interpreter. As such a file doesn't require lots of the checks needed for a pure source file,
the execution starts faster, and runs faster, too.
• Thanks to that, every subsequent import will go quicker than interpreting the source text from scratch.
• Python is able to check if the module's source file has been modified (in this case, the pyc file will be
rebuilt) or not (when the pyc file may be run at once). As this process is fully automatic and
transparent, you don't have to keep it in mind.
• Your first module: step 3
• Now we've put a little something into the module file:
• Updating the module.py file
print("I like to be a module.")
• Can you notice any differences between a module and an ordinary script? There are none so
far.
• It's possible to run this file like any other script. Try it for yourself.
• What happens? You should see the following line inside your console:
• I like to be a module.
• Your first module: step 4
• Let's go back to the main.py file:
import module
Run it. What do you see? Hopefully,
you see something like this:
I like to be a module.
• To create a modules in python perform the following steps
• 1st-Create a folder named as module in any drive of your PC
• 2nd- create a python program file named as main.py
• And add the program in it
• print(“welcome to new module”) and save it in new created folder.
• 3rd- create another python file named as import.py save it in the same created folder module and
• In that write the following commands
• Import main
• And save it run it on python to see the output.
14
• What does it actually mean?
• When a module is imported, its content is implicitly executed by Python. It gives the module the
chance to initialize some of its internal aspects (e.g., it may assign some variables with useful
values).
• Note: the initialization takes place only once, when the first import occurs, so the assignments done
by the module aren't repeated unnecessarily.
• Imagine the following context:
• there is a module named mod1;
• there is a module named mod2 which contains the import mod1 instruction;
• there is a main file containing the import mod1 and import mod2 instructions.
• At first glance, you may think that mod1 will be imported twice - fortunately, only the first import
• Your first module: step 5
• Python can do much more. It also creates a variable called __name__.
• Moreover, each source file uses its own, separate version of the variable - it isn't shared
between modules.
• We'll show you how to use it. Modify the module a bit:
print("I like to be a module.")
print(__name__)
• Now run the module.py file. You should see the following lines:
• I like to be a module
• __main__
• output
• Now run the main.py file. And? Do you see the same as us?
• I like to be a module
• module
• We can say that:
• when you run a file directly, its __name__ variable is set to __main__;
• when a file is imported as a module, its __name__ variable is set to the file's name
(excluding .py)
• Your first module: step 6
• This is how you can make use of the __main__ variable in order to detect the context in which
your code has been activated:
if __name__ == "__main__":
print("I prefer to be a module.")
else:
print("I like to be a module.")
• There's a cleverer way to utilize the variable, however. If you write a module filled with a number
of complex functions, you can use it to place a series of tests to check if the functions work
properly.
• Each time you modify any of these functions, you can simply run the module to make sure that
your amendments didn't spoil the code. These tests will be omitted when the code is imported as a
module.
• Your first module: step 7
• This module will contain two simple functions, and if you want to know how many times the
functions have been invoked, you need a counter initialized to zero when the module is being
imported.
• You can do it this way:
counter = 0
if __name__ == "__main__":
print("I prefer to be a module.")
else:
print("I like to be a module.")
• Your first module: step 8
• Introducing such a variable is absolutely correct, but may cause important side effects that you
must be aware of.
• Take a look at the modified main.py file:
import module
print(module.counter)
• As you can see, the main file tries to access the module's counter variable. Is this legal? Yes, it
is. Is it usable? It may be very usable. Is it safe?
• That depends - if you trust your module's users, there's no problem; however, you may not want
the rest of the world to see your personal/private variable.
• Unlike many other programming languages, Python has no means of allowing you to hide such
variables from the eyes of the module's users.
• You can only inform your users that this is your variable, that they may read it, but that they
should not modify it under any circumstances.
• Your first module: step 9
• Okay. Let's write some brand new code in our module.py file. The updated module is ready
here:
• __counter = 0 # Global counter variable
• def suml(the_list):
• """
• Calculate the sum of elements in a list.
• Parameters:
• the_list (list): A list of numeric values.
• Returns:
• int or float: The sum of the elements in the_list.
• """
• global __counter
• __counter += 1
• the_sum = 0
• for element in the_list:
• the_sum += element 27
• def prodl(the_list):
• """
• Calculate the product of elements in a list.
• Parameters:
• the_list (list): A list of numeric values.
• Returns:
• int or float: The product of the elements in the_list.
• """
• global __counter
• __counter += 1
• the_product = 1
• for element in the_list:
• the_product *= element
• return the_product 28
• if __name__ == "__main__":
• print("Running tests...")
• my_list = [i+1 for i in range(5)]
• print(suml(my_list) == 15) # True if sum is correct
• print(prodl(my_list) == 120) # True if product is correct
• print(f"Function calls: {__counter}") # Print the number of
function calls
29
• Your first module: step 10
• Now it's possible to use the updated module - this is one way:
from module import suml, prodl
zeroes = [0 for i in range(5)]
ones = [1 for i in range(5)]
print(suml(zeroes))
print(prodl(ones))
• Your first module: step 11
• It's time to make our example more complicated - so far we've assumed that the main Python
file is located in the same folder/directory as the module to be imported.
• Let's give up this assumption and conduct the following thought experiment:
• we are using Windows ® OS (this assumption is important, as the file name's shape depends
on it)
• the main Python script lies in C:\Users\user\py\progs and is named main.py
• the module to import is located in C:\Users\user\py\modules
• How to deal with it?
• To answer this question, we have to talk about how Python searches for modules. There's a special
variable (actually a list) storing all locations (folders/directories) that are searched in order to find
a module which has been requested by the import instruction.
• Python browses these folders in the order in which they are listed in the list - if the module cannot
be found in any of these directories, the import fails.
• Otherwise, the first folder containing a module with the desired name will be taken into
consideration (if any of the remaining folders contains a module of that name, it will be ignored).
• The variable is named path, and it's accessible through the module named sys. This is how you can
check its regular value:
import sys
for p in sys.path:
print(p)
• We've launched the code inside the C:\User\user folder, and this is what we've got:
• C:\Users\user
• C:\Users\user\AppData\Local\Programs\Python\Python36-32\python36.zip
• C:\Users\user\AppData\Local\Programs\Python\Python36-32\DLLs
• C:\Users\user\AppData\Local\Programs\Python\Python36-32\lib
• C:\Users\user\AppData\Local\Programs\Python\Python36-32
• C:\Users\user\AppData\Local\Programs\Python\Python36-32\lib\site-packag
• Note: the folder in which the execution starts is listed in the first path's element.
• Note once again: there is a zip file listed as one of the path's elements - it's not an error. Python is
able to treat zip files as ordinary folders - this can save lots of storage.
• Can you figure out how we can solve our problem now? We can add a folder containing the
module to the path variable (it's fully modifiable).
• Your first module: step 12
• One of several possible solutions looks like this:
from sys import path
path.append('..\\modules')
import module
zeroes = [0 for i in range(5)]
ones = [1 for i in range(5)]
print(module.suml(zeroes))
print(module.prodl(ones))
• Note:
• we've doubled the \ inside folder name - do you know why?
• Check
• we've used the relative name of the folder - this will work if you start the main.py file directly
from its home folder, and won't work if the current directory doesn't fit the relative path; you can
always use an absolute path, like this:
• path.append('C:\\Users\\user\\py\\modules')
• we've used the append() method - in effect, the new path will occupy the last element in the path
list; if you don't like the idea, you can use insert() instead.
Your first package: step 1
• Imagine that in the not-so-distant future you and your associates write a large number of
Python functions.
• Your team decides to group the functions in separate modules, and this is the final result of the
ordering:
• #! /usr/bin/env python3
• """ module: alpha """
• def funA():
• return "Alpha"
• if __name__ == "__main__":
• print("I prefer to be a module.")
• Note: we've presented the whole content for the alpha.py module only
- assume that all the modules look similar (they contain one function
named funX, where X is the first letter of the module's name).
• Your first package: step 2
• Suddenly, somebody notices that these modules form their own hierarchy, so putting them all
in a flat structure won't be a good idea.
• After some discussion, the team comes to the conclusion that the modules have to be grouped.
All participants agree that the following tree structure perfectly reflects the mutual
relationships between the modules:
from sys import path
path.append('..\\packages')
import extra.iota
print(extra.iota.funI())
from sys import path
path.append('..\\packages')
from extra.iota import funI
print(funI())
from sys import path
path.append('..\\packages')
import extra.good.best.sigma
from extra.good.best.tau import funT
print(extra.good.best.sigma.funS())
print(funT())
• from sys import path
• path.append('..\\packages\\extrapack.zip')
• import extra.good.best.sigma as sig
• import extra.good.alpha as alp
• from extra.iota import funI
• from extra.good.beta import funB
• print(sig.funS())
• print(alp.funA())
• print(funI())
• print(funB())
Review of the Day
3rd floor Prabhakar Plaza, Station Road, Kolhapur.
Pin -416416
Mobile Number-9225804090