Organizing code: imports, modules, packages

Modules in Python

Modules in Python

Overview

Modules allows to split a long program into several files
for instance - to put function/class definitions in one or more files, and the main program to be written in a separate file.
A module in Python is simply a file containing Python definitions and statements. The file name is the module name with the suffix .py added.
Module == Python file
To create a module, you just write Python code in a .py file

                ├── my_app
                │   ├── app.py           # the main file
                │   ├── my_module.py     # a module file
            

                def greet(name):
                    return f"Hello, {name}!"
            
To use this module in another Python script you must import it:

                import my_module

                # Using the function from the module
                print(my_module.greet("World"))  # Output: Hello, World!
            
Note that a module creates its own namespace, so we must prefix the imported names with module name

Import entire module: import X

To use a module, you have to import it first.
As a module generates its own namespace, you have to prefix the imported names with the module name:

                def get_user_name():
                    return input('Enter your name:')

                def greet(user_name):
                    print(f'Hello, {user_name}!')
            

                # import the helper_module:
                import helper_module

                # use helper_module
                user_name = helper_module.get_user_name()
                helper_module.greet(user_name)
            

Module alias: import X as Y

You can give modules names a shorter aliases:

            import helper_module as hm

            user_name = hm.get_user_name()
            hm.greet(user_name)
        

Import object from module: from X import Y

You can import an object from module, using the
from X import Y notation. After that, you can use the name (Y), without having to prefixing it.
You can import more that one object, if you separate them with comma

                from helper_module import get_user_name, greet

                user_name = get_user_name()
                greet(user_name)
            

Import object from module: from X import *

Imports all objects from a module.
Not recommended to use it, as a name collision could occurs.

            from helper_module import *

            user_name = get_user_name()
            greet(user_name)
        

Where import looks for a module?

When you import a module, Python searches for it in:
The current directory (the directory from which you're running your Python script or starting the Python interpreter.)
the directories specified in the PYTHONPATH environment variable
Standard library directories
You can check the search path:

                import sys
                print(sys.path)
            

Module Execution and Caching

When you import a module, Python executes all the code in that module
Python imports a module only once per interpreter session. Subsequent import statements use the cached module

                import my_module  # Executes the module code
                import my_module  # Does nothing, uses cached module
            

Python's built-in modules

or why Python is "Batteries Included"
Python Module Index
to use a built-in (standard) module, you have to import it!

Python's third-party modules

The Python Packaging Index PyPIis a public repository of open source licensed packages made available for use by other Python users.
We are going to see how to install third-party modules after we get familiar with Python's Virtual Environments

Import-related module attributes

Import-related module attributes/global variables

The __file__ and __name__ variables

Within a python file we can use the global __file__ and __name__ variables.
__file__ variable contains the pathname of the file from which the module was loaded
When a Python module is imported, __name__ is set to the module’s name, without the .py extension
When a Python module is executed as a standalone script, the __name__ variable is set to '__main__' value

            print( "__file__:", __file__)
            print( "__name__:", __name__)
        
Executed as a standalone script:

            python helper_module.py

            # __file__: /some/path/helper_module.py
            # __name__: __main__
        
Executed as a module:

            import helper_module

            # __file__: /some/path/helper_module.py
            # __name__: helper_module
        

__name__

When Python interpreter reads a .py file, it executes the code in it!
If a .py file is executed as a module:
__name__ is set to module's own filename
If a .py file is executed as stand-alone program:
__name__ is set to "__main__"

__name__ - examples

Create in same directory next Python files:


            import helper_module
        

            if __name__ == "__main__":
                print("helper_module is executed as stand-alone py file")
            else:
                print("helper_module is imported as module")
        
Run the main.py and look at the output
Run the helper_module.py and look at the output

__file__

Stores the full filename (including path) of the file/module being executed
Will be same, no matter if the file is executed as a stand-alone or as a module.
If we need to strip the path, and get just the filename,

__file__ - examples

Create in same directory next Python files:


            import helper_module
        

            print( "__file__:", __file__)
        
Run the main.py and look at the output
Run the helper_module.py and look at the output

Packages in Python

Packages in Python

Overview

A Python package is simply a group of Python module(s), usually stored in one directory
Packages are a way of structuring Python’s module namespace by using “dotted module names”.

make directory a package: __init__.py

You should put an __init__.py file in the directory, which you want it to be treated as package from Python.
__init__.py can be an empty file
or you can put some initialization code for the package

            $ tree my_app
            my_app/
            ├── app.py
            └── packA
                    ├── greet.py
                    ├── __init__.py
                    └── packB
                            ├── get_data.py
                            └── __init__.py
        

from python 3.2 __init__.py must be present only in Regular Packages, but can be omitted in Namespace Packages. More info in docs

Importing packages

You can import a module from a package using the same mechanism as importing a single module. But do not forget - a package makes its namespace, so you must use the dot notation.
If we have next structure:

                $ tree my_app
                my_app/
                ├── app.py
                └── packA
                        ├── greet.py
                        ├── __init__.py
                        └── packB
                                ├── get_data.py
                                └── __init__.py
            
We can import get_data module in app with:

                import packA.packB.get_data
            

pyc files and __pycahce__ folder

Python automatically compiles a script to so called byte code, before running it.
To speed up loading modules, when a module is imported for the first time, or when the source is more recent than the current compiled file, a .pyc file containing the compiled code will be created.
When you run the program next time, Python uses this file to skip the compilation step.
Python caches the compiled version of each module in the automatically created __pycache__ directory under the name module.version.pyc
If you really need to tell python not to generate the compiled versions, you can set the PYTHONDONTWRITEBYTECODE env. var.

Execute python module from command line

Execute python module from command line

We can use python -m module_name from terminal in order to execute a module

                python -m helper_module

                # note the difference between:
                python3 helper_module.py
            
Note that since the argument is a module name, you must not give a file extension (.py).

Homework

Homework

The tasks are given in next gist file
You can copy it and work directly on it. Just put your code under "### Your code here".

These slides are based on

customised version of

Hakimel's reveal.js

framework