A Review of Module Basics

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this lab, you will learn the basics of Python modules. Modules are Python files with function, class, and variable definitions that can be used in other Python programs. They help organize code into logical units and enhance reusability.

By the end of this lab, you will understand how to create your own modules, import them in various ways, and grasp important module loading behaviors that impact your code.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/BasicConceptsGroup -.-> python/variables_data_types("Variables and Data Types") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ModulesandPackagesGroup -.-> python/importing_modules("Importing Modules") python/ModulesandPackagesGroup -.-> python/creating_modules("Creating Modules") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") subgraph Lab Skills python/variables_data_types -.-> lab-132528{{"A Review of Module Basics"}} python/function_definition -.-> lab-132528{{"A Review of Module Basics"}} python/importing_modules -.-> lab-132528{{"A Review of Module Basics"}} python/creating_modules -.-> lab-132528{{"A Review of Module Basics"}} python/classes_objects -.-> lab-132528{{"A Review of Module Basics"}} end

Creating a Simple Module

Let's begin our journey into Python modules by creating a simple one. In Python, a module is essentially a file with a .py extension that holds Python code. Think of it as a container where you can group related functions, classes, and variables together. This makes your code more organized and easier to manage, especially as your projects grow in size.

  1. First, open the WebIDE. Once it's open, you'll need to create a new file. To do this, click on "File" in the menu bar, and then select "New File". Name this new file simplemod.py and save it in the /home/labex/project directory. This directory is where we'll keep all the files related to this experiment.

  2. Now, let's add some code to our newly created simplemod.py file. The code below defines a few basic elements that you'll commonly find in a Python module.

## simplemod.py

x = 42        ## A global variable

## A simple function
def foo():
    print('x is', x)

## A simple class
class Spam:
    def yow(self):
        print('Yow!')

## A scripting statement
print('Loaded simplemod')

In this code:

  • x = 42 creates a global variable named x and assigns it the value 42. Global variables can be accessed from anywhere within the module.
  • The foo() function is defined to print the value of the global variable x. Functions are reusable blocks of code that perform a specific task.
  • The Spam class is a blueprint for creating objects. It has a method called yow(), which simply prints the string 'Yow!'. Methods are functions that belong to a class.
  • The print('Loaded simplemod') statement is a scripting statement. It will execute as soon as the module is loaded, which helps us confirm that the module has been successfully loaded.
  1. After adding the code, save the file. You can do this by pressing Ctrl+S on your keyboard or by selecting "File" > "Save" from the menu. Saving the file ensures that all the changes you've made are preserved.

Let's take a closer look at what this module contains:

  • A global variable x with the value 42. This variable can be used throughout the module and even accessed from other modules if imported correctly.
  • A function foo() that prints the value of x. Functions are useful for performing repetitive tasks without having to write the same code multiple times.
  • A class Spam with a method yow(). Classes and methods are fundamental concepts in object - oriented programming, which allows you to create complex data structures and behaviors.
  • A print statement that executes when the module is loaded. This statement serves as a visual indicator that the module has been successfully loaded into the Python environment.

The print statement at the bottom will help us observe when the module is loaded, which is important for debugging and understanding how modules work in Python.

โœจ Check Solution and Practice

Importing and Using Modules

Now that we have created a module, it's time to understand how to import it and use its components. In Python, a module is a file containing Python definitions and statements. When you import a module, you gain access to all the functions, classes, and variables defined within it. This allows you to reuse code and organize your programs more effectively.

  1. First, we need to open a new terminal in the WebIDE. This terminal will serve as our workspace where we can run Python commands. To open a new terminal, click on "Terminal" > "New Terminal".

  2. Once the terminal is open, we need to start the Python interpreter. The Python interpreter is a program that reads and executes Python code. To start it, type the following command in the terminal and press Enter:

python3
  1. Now that the Python interpreter is running, we can import our module. In Python, we use the import statement to bring a module into our current program. Type the following command in the Python interpreter:
>>> import simplemod
Loaded simplemod

You'll notice that "Loaded simplemod" appears in the output. This is because the print statement in our simplemod module executes when the module is loaded. When Python imports a module, it runs all the top - level code in that module, including any print statements.

  1. After importing the module, we can access its components using dot notation. Dot notation is a way to access attributes (variables and functions) of an object in Python. In this case, the module is an object, and its functions, variables, and classes are its attributes. Here are some examples of how to access different components of the simplemod module:
>>> simplemod.x
42
>>> simplemod.foo()
x is 42
>>> spam_instance = simplemod.Spam()
>>> spam_instance.yow()
Yow!

In the first line, we access the variable x defined in the simplemod module. In the second line, we call the function foo from the simplemod module. In the third and fourth lines, we create an instance of the Spam class defined in the simplemod module and call its method yow.

  1. Sometimes, you might encounter an ImportError when trying to import a module. This error occurs when Python cannot find the module you are trying to import. To figure out where Python is looking for modules, you can examine the sys.path variable. The sys.path variable is a list of directories that Python searches when looking for modules. Type the following commands in the Python interpreter:
>>> import sys
>>> sys.path
['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

The first element in the list (the empty string) represents the current working directory. This is where Python looks for the simplemod.py file. If your module is not in one of the directories listed in sys.path, Python won't be able to find it, and you'll get an ImportError. Make sure your simplemod.py file is in the current working directory or one of the other directories in sys.path.

Understanding Module Loading Behavior

In Python, the way modules are loaded has some interesting characteristics. In this step, we'll explore these behaviors to understand how Python manages module loading.

  1. First, let's see what happens when we try to import a module again within the same Python interpreter session. When you start a Python interpreter, it's like opening a workspace where you can run Python code. Once you've imported a module, importing it again might seem like it would reload the module, but that's not the case.
>>> import simplemod

Notice that this time you do not see the "Loaded simplemod" output. This is because Python only loads a module once per interpreter session. Subsequent import statements do not reload the module. Python remembers that it has already loaded the module, so it doesn't go through the process of loading it again.

  1. After importing a module, you can modify the variables inside it. A module in Python is like a container that holds variables, functions, and classes. Once you've imported a module, you can access and change its variables just like you would with any other Python object.
>>> simplemod.x
42
>>> simplemod.x = 13
>>> simplemod.x
13
>>> simplemod.foo()
x is 13

Here, we first check the value of the variable x in the simplemod module, which is initially 42. Then we change its value to 13 and verify that the change has been made. When we call the foo function in the module, it reflects the new value of x.

  1. Importing the module again does not reset the changes we made to its variables. Even if we try to import the module once more, Python doesn't reload it, so the changes we made to its variables remain.
>>> import simplemod
>>> simplemod.x
13
  1. If you want to forcibly reload a module, you need to use the importlib.reload() function. Sometimes, you might have made changes to the module's code and want to see those changes take effect immediately. The importlib.reload() function allows you to do just that.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
>>> simplemod.x
42
>>> simplemod.foo()
x is 42

The module has been reloaded, and the value of x has been reset to 42. This shows that the module has been loaded again from its source code, and all the variables have been initialized as they were originally.

  1. Python keeps track of all loaded modules in the sys.modules dictionary. This dictionary acts as a registry where Python stores information about all the modules that have been loaded during the current interpreter session.
>>> 'simplemod' in sys.modules
True
>>> sys.modules['simplemod']
<module 'simplemod' from 'simplemod.py'>

By checking if a module name is in the sys.modules dictionary, you can see if the module has been loaded. And by accessing the dictionary with the module name as the key, you can get information about the module.

  1. You can remove a module from this dictionary to force Python to reload it on the next import. If you remove a module from the sys.modules dictionary, Python forgets that it has already loaded the module. So, the next time you try to import it, Python will load it again from its source code.
>>> del sys.modules['simplemod']
>>> import simplemod
Loaded simplemod
>>> simplemod.x
42

The module was loaded again because it was removed from sys.modules. This is another way to ensure that you're working with the latest version of a module's code.

Using from module import Syntax

In Python, there are various ways to import components from modules. One of these ways is the from module import syntax, which we'll explore in this section.

When you import components from a module, it's often a good idea to start with a clean slate. This ensures that there are no leftover variables or settings from previous interactions that could interfere with our current experiment.

  1. Restart the Python interpreter to get a clean state:
>>> exit()

This command exits the current Python interpreter session. After exiting, we'll start a new session to ensure a fresh environment.

python3

This bash command starts a new Python 3 interpreter session. Now that we have a clean Python environment, we can start importing components from a module.

  1. Import specific components from a module using the from module import syntax:
>>> from simplemod import foo
Loaded simplemod
>>> foo()
x is 42

Here, we're using the from simplemod import foo statement to import only the foo function from the simplemod module. Notice that even though we only asked for the foo function, the entire simplemod module was loaded. This is indicated by the "Loaded simplemod" output. The reason for this is that Python needs to load the whole module to access the foo function.

  1. When using from module import, you cannot access the module itself:
>>> simplemod.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'simplemod' is not defined

When we use the from module import syntax, we're only bringing in the specified components directly into our namespace. The module name itself is not imported. So, when we try to access simplemod.foo(), Python doesn't recognize simplemod because it wasn't imported in that way.

  1. You can import multiple components at once:
>>> from simplemod import x, foo
>>> x
42
>>> foo()
x is 42

The from module import syntax allows us to import multiple components from a module in a single statement. Here, we're importing both the variable x and the function foo from the simplemod module. After importing, we can directly access these components in our code.

  1. When you import a variable from a module, you are creating a new reference to the object, not a link to the variable in the module:
>>> x = 13  ## Change the local variable x
>>> x
13
>>> foo()
x is 42  ## The function still uses the module's x, not your local x

When we import a variable from a module, we're essentially creating a new reference to the same object in our local namespace. So, when we change the local variable x to 13, it doesn't affect the x variable inside the simplemod module. The foo() function still refers to the module's x variable, which is 42. Understanding this concept is crucial to avoid confusion in your code.

Exploring Module Reloading Limitations

Module reloading is a useful feature in Python, but it comes with some limitations, especially when dealing with classes. In this section, we'll explore these limitations step by step. Understanding these limitations is crucial for both development and production environments.

  1. Restart the Python interpreter:
    First, we need to restart the Python interpreter. This step is important because it ensures that we start with a clean slate. When you restart the interpreter, all previously imported modules and variables are cleared. To exit the current Python interpreter, use the exit() command. Then, start a new Python interpreter session using the python3 command in the terminal.
>>> exit()
python3
  1. Import the module and create an instance of the Spam class:
    Now that we have a fresh Python interpreter session, we'll import the simplemod module. Importing a module allows us to use the classes, functions, and variables defined in that module. After importing the module, we'll create an instance of the Spam class and call its yow() method. This will help us see the initial behavior of the class.
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Yow!
  1. Now let us modify the Spam class in our module. Exit the Python interpreter:
    Next, we're going to make changes to the Spam class in the simplemod module. Before we do that, we need to exit the Python interpreter. This is because we want to make changes to the source code of the module and then see how those changes affect the behavior of the class.
>>> exit()
  1. Open the simplemod.py file in the WebIDE and modify the Spam class:
    Open the simplemod.py file in the WebIDE. This is where the source code of the simplemod module is located. We'll modify the yow() method of the Spam class to print a different message. This change will help us observe how the behavior of the class changes after reloading the module.
## simplemod.py
## ... (leave the rest of the file unchanged)

class Spam:
    def yow(self):
        print('More Yow!')  ## Changed from 'Yow!'
  1. Save the file and return to the terminal. Start the Python interpreter and create a new instance:
    After making the changes to the simplemod.py file, save it. Then, return to the terminal and start a new Python interpreter session. Import the simplemod module again and create a new instance of the Spam class. Call the yow() method of the new instance to see the updated behavior.
python3
>>> import simplemod
Loaded simplemod
>>> t = simplemod.Spam()
>>> t.yow()
More Yow!
  1. Now let us demonstrate what happens with reloading:
    To see how module reloading works, we'll use the importlib.reload() function. This function allows us to reload a previously imported module. After reloading the module, we'll see if the changes we made to the Spam class are reflected.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
  1. Exit Python, modify the file again, and then test both instances:
    Exit the Python interpreter once more. Then, make another change to the Spam class in the simplemod.py file. After that, we'll test both the old and new instances of the Spam class to see how they behave.
>>> exit()
  1. Update the simplemod.py file:
    Open the simplemod.py file again and modify the yow() method of the Spam class to print a different message. This change will help us further understand the limitations of module reloading.
## simplemod.py
## ... (leave the rest of the file unchanged)

class Spam:
    def yow(self):
        print('Even More Yow!')  ## Changed again
  1. Save the file and return to the terminal:
    Save the changes to the simplemod.py file and return to the terminal. Start a new Python interpreter session, import the simplemod module, and create a new instance of the Spam class. Call the yow() method of the new instance to see the updated behavior.
python3
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Even More Yow!

>>> ## Exit without closing Python, edit the file
  1. Without closing Python, open simplemod.py in the WebIDE and change it:
    Without closing the Python interpreter, open the simplemod.py file in the WebIDE and make another change to the yow() method of the Spam class. This will help us see how the behavior of existing and new instances changes after reloading the module.
## simplemod.py
## ... (leave the rest of the file unchanged)

class Spam:
    def yow(self):
        print('Super Yow!')  ## Changed one more time
  1. Save the file and go back to the Python interpreter:
    Save the changes to the simplemod.py file and go back to the Python interpreter. Reload the simplemod module using the importlib.reload() function. Then, test both the old and new instances of the Spam class to see how they behave.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>

>>> ## Try the old instance
>>> s.yow()
Even More Yow!  ## Still uses the old implementation

>>> ## Create a new instance
>>> t = simplemod.Spam()
>>> t.yow()
Super Yow!  ## Uses the new implementation

Notice that the old instance s still uses the old implementation, while the new instance t uses the new implementation. This happens because reloading a module does not update existing instances of classes. When a class instance is created, it stores a reference to the class object at that time. Reloading the module creates a new class object, but the existing instances still refer to the old class object.

  1. You can also observe other unusual behaviors:
    We can further observe the limitations of module reloading by using the isinstance() function. This function checks if an object is an instance of a particular class. After reloading the module, we'll see that the old instance s is no longer considered an instance of the new simplemod.Spam class, while the new instance t is.
>>> isinstance(s, simplemod.Spam)
False
>>> isinstance(t, simplemod.Spam)
True

This indicates that after reloading, simplemod.Spam refers to a different class object than the one used to create s.

These limitations make module reloading useful primarily for development and debugging, but not recommended for production code. In a production environment, it's important to ensure that all instances of a class use the same, up - to - date implementation. Module reloading can lead to inconsistent behavior, which can be difficult to debug and maintain.

Summary

In this lab, you have learned the fundamentals of working with modules in Python. You've learned how to create a Python module as a .py file with variables, functions, and classes, and how to import modules using the import statement. You also understand that Python loads modules only once per interpreter session and can use importlib.reload() to force reloading.

Moreover, you've explored the sys.modules dictionary for tracking loaded modules, used the from module import syntax to import specific components, and grasped the limitations of module reloading, especially with classes. These concepts are the foundation for organizing Python code into reusable components, which are essential for maintaining code structure and promoting reusability in larger applications.