๐Ÿ“ฆimport, from, __name__, packages, __init__.pyLESSON

Python Modules & Packages

Python's module system is how code is organized and reused. A module is any .py file. A package is a directory containing an __init__.py file. Understanding how Python finds and loads code is essential for building larger projects.

The import Statement

The simplest way to use another module:

When you write import math, Python:

  1. Searches sys.path for a file or package named math
  2. Executes the module's code (once โ€” subsequent imports use the cached version in sys.modules)
  3. Binds the name math in your local namespace to the module object

from module import name

Import specific names directly into your namespace:

The wildcard form from math import * imports all names not starting with underscore, but this is discouraged because it pollutes your namespace and makes it hard to tell where names come from.

Aliasing with as

Rename imports to avoid conflicts or shorten long names:

The as keyword works for both import and from ... import:

The __name__ Variable

Every Python module has a built-in __name__ attribute. When a module is run directly as a script, __name__ is set to "__main__". When imported by another module, __name__ is the module's file name (without .py).

This enables the classic guard pattern:

This pattern lets a file be both an importable module and a runnable script. It's one of Python's most important idioms.

Packages: Modules in Directories

A package is a directory containing an __init__.py file (which can be empty):

Import from the package:

__init__.py: The Package Initializer

__init__.py runs when the package is imported. You can use it to:

  1. Re-export names for a cleaner public API:
  1. Run initialization code (connect to DB, load config, etc.)

  2. Define __all__ to control what from package import * exports

Relative Imports

Inside a package, you can use relative imports with dot notation:

Relative imports only work inside packages โ€” not in top-level scripts. They make refactoring easier since package internals don't need to know the package's name.

__all__: Controlling Public API

Define __all__ as a list of strings to specify what names are exported:

When someone does from utils.strings import *, only the names in __all__ are imported.

sys.path: How Python Finds Modules

Python searches for modules in this order:

  1. The directory of the script being run (or current directory in the REPL)
  2. Directories in the PYTHONPATH environment variable
  3. Installation-dependent default locations (site-packages, stdlib)

You can inspect and modify sys.path at runtime:

importlib: Dynamic Imports

For dynamic imports (when you don't know the module name until runtime), use importlib:

This is used internally by plugin systems, dependency injection frameworks, and test runners.

Module Caching

Python caches imported modules in sys.modules. Subsequent import statements for the same module return the cached version without re-executing the module file:

Best Practices

  • Group imports: stdlib first, then third-party, then local imports
  • Use absolute imports in application code for clarity
  • Use relative imports inside packages for internal references
  • Always use the __name__ == "__main__" guard in scripts
  • Define __all__ in modules that are meant to be public APIs
  • Avoid circular imports โ€” restructure code if modules import each other

Knowledge Check

What is the value of `__name__` when a Python file is run directly as a script (e.g., `python myscript.py`)?

What is the purpose of `__init__.py` in a Python package directory?

In a relative import like `from ..utils import helper`, what does `..` mean?