As you write Python programs, you will quickly find yourself re-using the same pieces of code – functions for calculating dates, reading files, or handling text. Instead of writing that code from scratch each time, you can store it in a file called a module and then 'import' it into your current program. Understanding how to organise and import these modules is a core skill for the PCAP-31-03 exam, because it tests how you manage code structure and avoid naming conflicts.
Jump to a section
A simple way to picture Modules and Packages: Importing and Namespaces
A large office with a central filing cabinet. The cabinet has many drawers, and each drawer is labelled for a different department: Sales, Marketing, IT, HR. Inside each drawer are folders, and inside each folder are individual documents.
A new employee, Alex, needs a specific sales report from the Sales department. Instead of walking to the Sales desk and asking them to hand over a copy (which could cause delays if they are busy, or confusion if they hand over the wrong report), Alex follows the office procedure. Alex opens the central filing cabinet drawer for Sales, finds the folder labelled 'Monthly Reports', and takes the specific document from that folder. Importantly, Alex does not dump all the Sales documents onto their own desk. They only take what they need. The office also has a rule: if a folder has a special yellow tag on it (like a __init__.py file), Alex knows that folder is a 'special department bundle' and contains extra instructions for how to access the files inside.
This is exactly how Python modules and packages work. The filing cabinet is the Python library. The drawer is a package (Sales). The folder is a sub-package (Monthly Reports). The document is a module (the sales report code). The office procedure is the import statement. The special yellow tag is the __init__.py file, which tells Python, 'This folder is a package, not just a random collection of files'. Alex's own desk is the current namespace – they only bring in the exact items they need, without cluttering their workspace.
In Python, a module is simply a single file that ends with .py. You can think of it as a recipe book – one file containing a collection of related functions, variables, and classes. For example, you might create a file called weather_utils.py that contains a function called get_temperature(city). To use that function in another program, you use the import statement. The simplest form is import weather_utils. When Python runs this line, it does two things: it runs all the code inside weather_utils.py, and it creates a new namespace (a container for names) called weather_utils. To call your function, you must then write weather_utils.get_temperature("London"). This is called 'qualifying' the name – you are explicitly telling Python to look inside the weather_utils namespace for the function. The benefit is that if you also have a function called get_temperature in your main program, they will not clash, because they live in different namespaces.
But what if you want to bring the function directly into your current namespace so you can call it as just get_temperature("London")? You use a different form: from weather_utils import get_temperature. This copies the function reference into your current namespace. The downside is that if you already have a variable named get_temperature in your main program, it will be overwritten. For PCAP-31-03, you must memorise the three main import forms: import modulename, from modulename import item, and from modulename import *. The asterisk (*) imports all names that the module defines at the top level, which is generally discouraged because it pollutes your namespace and can cause accidental name collisions.
Now, as you accumulate many modules, you will want to organise them into folders. A folder containing multiple .py files is called a package. For Python to recognise a folder as a package, the folder must contain a file named __init__.py. This file can be empty, but it signals to Python that the folder is a package that can be imported. For example, let us create a folder called shapes. Inside it, we have circle.py (with a function area(radius)) and square.py (with a function area(side)). Inside the shapes folder, we create an empty file named __init__.py. Now, from another script, we can write:
import shapes.circle and then call shapes.circle.area(5)
Or from shapes.circle import area and then call area(5) directly.
The __init__.py file can also contain code that runs when the package is first imported. For instance, you might use it to set up shared variables or to automatically import certain sub-modules so they are available immediately: from . import circle inside __init__.py would make circle available as shapes.circle without an extra explicit import.
Python has a built-in search path for modules, called sys.path. When you write import weather_utils, Python looks for a file named weather_utils.py in a list of directories. The first place it looks is the directory containing the script that was run (the current working directory). Then it checks every directory listed in the PYTHONPATH environment variable, and finally it checks standard library directories and site-packages (where third-party modules are installed). If Python cannot find the module, it raises a ModuleNotFoundError. This is a common exam trap – they might ask what happens when you try to import a module that is not in any of these locations.
Namespaces are crucial to understand. Every time you import a module, Python creates a new namespace object. When you assign a variable inside a module, that variable lives in the module's namespace. When you import using from modulename import item, the item is copied into your current namespace (the global namespace of your script). The original item in the module’s namespace remains unchanged. If you change the value of the imported item in your script, you are only changing your local copy – not the original in the module. However, if you import the module itself (import modulename) and then modify an attribute of that module (e.g., modulename.some_variable = 10), that change is visible to any other code that also imports the same module and reads that attribute. This behaviour is tested in the exam.
Finally, the __name__ variable. When you run a Python script directly, Python sets the special variable __name__ to "__main__". When a module is imported from another script, __name__ is set to the module's name (e.g., "weather_utils"). This lets you write code that only runs when the file is executed directly, not when it is imported, using the pattern if __name__ == "__main__":. The exam often asks about this.
To summarise the hierarchy: a package is a folder with __init__.py. A sub-package is a sub-folder also containing __init__.py. A module is a .py file inside any package. A function or class is defined inside a module. Each level lives in its own namespace.
Create the module file
Create a new text file with the `.py` extension, for example `my_functions.py`. Inside, write your Python code – functions, classes, variables. This file is now a module. It must be in a directory that Python can find (either the same directory as your main script, or a directory listed in `sys.path`).
Create the package folder
Create a new directory, for example `my_package`. Inside this directory, create an empty file named `__init__.py`. This file tells Python that this directory is a package. Without it, Python will not treat the folder as a package and you will not be able to use dot notation to import modules inside it.
Add sub-modules to the package
Place additional `.py` files inside the `my_package` directory, such as `module_a.py` and `module_b.py`. These are now sub-modules of the package `my_package`. You can also create sub-packages by adding sub-directories, each containing their own `__init__.py` file.
Write the import statement in your main script
In your main script, use `import my_package.module_a` to access `module_a` via the qualified name `my_package.module_a`. Alternatively, use `from my_package.module_a import my_function` to bring `my_function` directly into your current namespace. If the module is not in `sys.path`, you will get a `ModuleNotFoundError`.
Handle namespace conflicts with aliasing
If two imported modules have functions with the same name, use the `as` keyword to rename them: `from my_package.module_a import my_function as func_a` and `from other_module import my_function as func_b`. This keeps your namespace clean and avoids accidental overwrites.
Prevent execution of test code on import
Inside your module file, any code not inside a function or class runs immediately when the module is imported. To prevent test code from running on import, wrap it inside `if __name__ == "__main__":`. This way, the test code only runs when you execute the module file directly, not when it is imported elsewhere.
Optimise package initialisation in __init__.py
Inside `__init__.py`, you can import specific sub-modules so that they become available directly as attributes of the package. For example, writing `from . import module_a` in `__init__.py` allows you to later `import my_package` and then access `my_package.module_a` directly, without an explicit `import my_package.module_a`.
Imagine you are a junior developer at a company that builds a web application for managing customer orders. The application is large, with hundreds of Python files. Without modules and packages, you would have a single massive script with tens of thousands of lines, making it impossible to maintain or collaborate. Your senior developer asks you to organise the code.
First, you create a top-level package called order_system. Inside it, you create an __init__.py file (just an empty file for now). Then you create sub-packages: order_system.models (for database-related code), order_system.views (for user interface logic), and order_system.utils (for helper functions like date parsing and email sending). Each sub-package also gets its own __init__.py file.
Inside order_system.models, you have a module called customer.py containing a Customer class. Inside order_system.views, you have order_view.py which needs to use the Customer class. So you write in order_view.py: from order_system.models.customer import Customer. But there is a catch: the project is deployed on multiple servers, and the developers need to ensure that the same structure works across all environments. You decide to make the __init__.py file in order_system.models import the Customer class directly, so that other parts of the application can simply write from order_system.models import Customer. This simplifies the import paths. Your __init__.py looks like:
from .customer import CustomerNow, a bug emerges: the Customer class needs a helper function called validate_email that lives in order_system.utils. You import it inside customer.py using from order_system.utils import validate_email. This creates a circular import risk if utils also tries to import from models. To avoid this, you restructure: you move validate_email into its own small module order_system.utils.email_utils.py and import it only inside the specific functions that need it, not at the top level of customer.py.
In your day-to-day work, you frequently need to test individual modules. You use the if __name__ == "__main__": pattern to write quick test code inside each module. For example, inside customer.py, you add:
if __name__ == "__main__":
c = Customer("Alice", "alice@example.com")
print(c)This lets you run python order_system/models/customer.py directly to test that specific module, without affecting the rest of the system when it is imported normally.
When the project grows, you encounter name clashes. Another library you use, analytics_toolkit, also has a Customer class. To avoid conflict, you use the fully qualified import: from order_system.models.customer import Customer as OrderCustomer and from analytics_toolkit import Customer as AnalyticsCustomer. This 'rename on import' (using as) is a practical technique the exam expects you to know.
Finally, when deploying to production, you must ensure all __init__.py files exist, even if empty. New developers often forget this, leading to ModuleNotFoundError. As a professional, you run a linter or a pre-commit hook to check that every package directory contains __init__.py.
The PCAP-31-03 exam objective 4.1 specifically tests your ability to create modules and packages, import using various methods, and understand package structure with __init__.py. Here is exactly what you need to know.
First, you must memorise the three primary import statement forms:
import modulename
from modulename import item
from modulename import *
You must also know that import modulename as alias and from modulename import item as alias are valid and commonly used. The exam will show you code snippets and ask what happens when they are executed. Common trap: they give you a module that defines a global variable, and then they import it two different ways, and ask whether changing the variable in the main script affects the module's original value. The answer is: if you do import modulename and then modulename.x = 5, the module's x changes globally across all importers. If you do from modulename import x and then x = 5, you are creating a new local variable x that shadows the imported one, and the module's original x remains unchanged.
Second, the __init__.py file. The exam loves to ask what makes a directory a package. The answer: a directory must contain an __init__.py file (or, in Python 3.3+, it can be a namespace package without it, but for PCAP-31-03, assume the classic behaviour that __init__.py is required). They will ask what happens if you try to import a module from a directory that lacks __init__.py. Answer: it still might work if the module is directly in sys.path (like a single .py file), but if the directory is meant to be a sub-package, the import will fail with ModuleNotFoundError. They may also ask about the role of __init__.py – it is executed when the package is first imported, and can be used to initialise package-level variables or automatically import sub-modules.
Third, the search path (sys.path). The exam tests your understanding of where Python looks for modules. Be able to list the order: 1) the directory containing the script, 2) any directories listed in PYTHONPATH environment variable, 3) standard library directories, 4) site-packages. A typical question: 'If you have two files with the same name in two different directories in sys.path, which one is imported?' The answer: the first one found.
Fourth, the __name__ variable. You must know that __name__ is set to "__main__" when a file is run directly, and to the module's name when imported. Questions ask what code inside an if __name__ == "__main__": block will do – it only runs during direct execution, not during import.
Fifth, package structure: you need to understand relative imports (using dots). For example, inside a package shapes, a module shapes/circle.py can import a sibling module shapes/square.py using from . import square (single dot for same package) or from .square import area (for a specific item). Double dots (..) go to the parent package. The exam tests that relative imports only work inside a package (i.e., they are used from within a module that is part of a package, not from a top-level script).
Specific trap patterns include:
Importing a module that itself imports something that causes a circular import (two modules importing each other). Python will raise an ImportError at runtime if a circular import is detected during the import process.
Forgetting to include __init__.py in a sub-package, then trying to import a module from that sub-package.
Using from module import * when the module does not define __all__. In that case, all names not beginning with an underscore are imported. If __all__ is defined, only those names in the list are imported.
Thinking that from module import * imports all sub-modules – it does not, only top-level names from that module's namespace.
Key definitions to memorise:
Module: a single .py file.
Package: a directory containing __init__.py and zero or more modules or sub-packages.
Namespace: a mapping from names to objects; each module has its own namespace.
__all__: a list of strings defining the public objects when using from module import *.
sys.path: the list of directories Python searches for modules.
Finally, be prepared for multi-select questions where you choose all valid import statements. They might mix correct syntax with incorrect ones like import module.submodule (valid) versus import module.submodule.attribute (invalid – you can only import modules or packages, not attributes, with the simple import statement). Use from module import attribute for attributes.
A module is a single `.py` file, and a package is a directory containing an `__init__.py` file plus zero or more modules or sub-packages.
Use `import modulename` to get a qualified namespace, and `from modulename import item` to bring an item directly into your current namespace.
The `__init__.py` file is executed when its package is first imported, and it can be used to initialise package-level resources or import sub-modules automatically.
Python locates modules by searching the directories listed in `sys.path` in order: the script's directory, then `PYTHONPATH`, then standard library directories, then site-packages.
The `__name__` variable equals `"__main__"` when a file is run directly, and equals the module's name when it is imported, enabling conditional execution of test code.
Using `from module import *` imports all names defined at the top level of that module that do not start with an underscore, unless the module defines an `__all__` list which then restricts what is imported.
These come up on the exam all the time. Here's how to tell them apart.
import modulename
Creates a separate namespace for the module
Requires prefix to access items: modulename.item
Safer for avoiding name collisions
from modulename import item
Copies the item into the current namespace
No prefix needed when using the item
Higher risk of overwriting existing names in current namespace
Module (single .py file)
A single file that can be imported directly
No special marker file needed
Cannot contain sub-modules within itself
Package (directory with __init__.py)
A directory that can contain multiple modules and sub-packages
Requires __init__.py to signal it is a package
Allows hierarchical organisation with dot notation
__name__ == "__main__" (direct run)
Occurs when the file is executed directly as a script
Code inside the `if` block runs
Useful for testing or running the module standalone
__name__ == module name (imported)
Occurs when the file is imported by another script
Code inside the `if` block is skipped
Prevents test code from executing unintentionally
sys.path (module search path)
A list of directories Python searches for modules
Can be modified programmatically with sys.path.append()
Includes standard library and site-packages directories
Current working directory
The directory from which the top-level script was run
Always the first entry in sys.path
Not the same as the directory of the imported module's file
Absolute import (e.g., from package.module import item)
Uses the full dotted path from the top-level package
Works regardless of where the importing module is located
Clear and unambiguous, recommended for most cases
Relative import (e.g., from . import sibling)
Uses dots to refer to current (.) or parent (..) packages
Only works inside a package, not from a top-level script
Can break if the package structure is reorganised
Mistake
If I create a folder and put my .py files in it, I can import them using 'import folder.filename'.
Correct
For a folder to be importable as a package, it must contain an `__init__.py` file (even if empty). Without it, Python will not treat the folder as a package and will raise a `ModuleNotFoundError` when you try to import using the dot notation, unless the folder is in `sys.path` and you import the file directly without the folder prefix.
Beginners think folders are automatically recognised as packages because that is how most file systems work – they assume the OS directory structure directly maps to import paths. But Python needs the explicit marker file `__init__.py` to treat a directory as a package, to prevent accidentally importing directories that are not meant to be Python packages.
Mistake
`from module import *` imports all the sub-modules and sub-packages inside that module.
Correct
`from module import *` only imports the names that are defined at the top level of that module's namespace, such as functions, classes, and variables. It does not automatically import sub-modules or sub-packages unless they are explicitly imported in the module's `__all__` list or the module's code itself imports them at the top level.
This mistake comes from assuming the asterisk is a 'wildcard' that recursively grabs everything, similar to how `*` works in shell globbing. In Python, `*` only applies to the names directly in that module's namespace, not the file system structure.
Mistake
When I import a module using `import modulename`, all the functions and variables become directly available without needing to prefix them with `modulename.`.
Correct
Using `import modulename`, you must use the qualified name `modulename.function()` to access its contents. To make them available directly, you must use `from modulename import function`.
Beginners often expect that importing a module is like copying all its contents into the current file, because that is conceptually simpler. But Python deliberately uses namespaces to avoid name collisions, and the qualified access is how that protection works.
Mistake
If I import a module in two different files, Python loads and executes the module twice, once for each file.
Correct
Modules are cached in `sys.modules`. When a module is imported for the first time, it is loaded and executed once, and its object is stored in a dictionary. Any subsequent imports of the same module, even from different files, just retrieve the cached object from `sys.modules`. The module code is not re-executed.
This misconception comes from thinking of imports as distinct operations per file, like how including a header file in C works. Python's caching mechanism is a performance optimisation that beginners do not intuitively expect.
Mistake
Changing a variable that I imported using `from module import x` inside my script will change the original variable inside the module for everyone else.
Correct
When you use `from module import x`, you get a reference to the object that `x` points to in the module at that moment. If you reassign `x` in your script, you are just pointing your local name `x` to a new object; the module's original `x` remains unchanged. Only if you use `module.x = new_value` (with the module reference) do you change the original.
This confusion arises because beginners do not distinguish between rebinding a name and mutating an object. They think 'import x' creates a link back to the module, but it is a one-time copy of the reference.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Check that the file has the `.py` extension and that you are not accidentally trying to import a module from a different working directory. Also ensure you have an `__init__.py` file if you are trying to import from a sub-directory package.
`import modulename` gives you access to everything in the module, but you must prefix names with `modulename.`. `from modulename import item` brings that specific item into your current namespace so you can use it directly without a prefix.
Yes, but Python will import only the first one it finds in `sys.path`. To avoid conflicts, use packages with `__init__.py` to create a hierarchy, and import using the full dotted path like `from mypackage.mymodule import something`.
It checks whether the current file is being run directly (as a script) or being imported as a module. The code inside that block runs only when the file is executed directly, not when it is imported into another script.
You can modify `sys.path` in your script by adding the parent directory: `import sys; sys.path.append("..")`. Then you can import the module normally. Alternatively, restructure your project into a package and use relative imports.
Yes, it is generally discouraged because it imports all names from the module into your namespace, which can accidentally overwrite your existing variables and make it unclear where a name came from. It is better to import only what you need explicitly.
You've finished Modules and Packages: Importing and Namespaces. Continue through the PCAP-31-03 study guide to build a complete picture of the exam.
Done with this chapter?