Skip to content

Chapter 4 of 8

Functions, Decorators, and Modules

Functions are first-class objects that can be wrapped to modify behavior through decorators. The `@decorator` syntax above a definition is syntactic sugar equivalent to `f = decorator(f)`. `functools.wraps` preserves the original function's metadata — name, docstring, and other attributes — when decorating. Python's argument model supports flexible calling conventions: `*args` captures positional arguments as a tuple, `**kwargs` captures keyword arguments as a dictionary, and the same `*` and `**` operators unpack iterables and dicts when invoking a function. Parameters before `/` are positional-only, while parameters after `*` are keyword-only, controlling how callers may invoke the function.

Scope rules follow the LEGB order: Local, Enclosing, Global, and Built-in. The `global` keyword rebinds a name at module level, while `nonlocal` rebinds it in the nearest enclosing function scope. Closures capture variables from enclosing scopes, but reassigning them requires `nonlocal` — otherwise the assignment creates a new local variable instead of modifying the captured one. For copying compound objects, `copy.copy()` performs a shallow copy that duplicates the outer container but shares inner references, while `copy.deepcopy()` recursively duplicates everything. The `functools` module provides `lru_cache` for memoizing results by argument, `cache` (without a size limit), `partial` for pre-filling arguments, and `reduce` for cumulative application of a function.

Module organization follows several conventions. `import module` preserves the module's namespace, while `from module import name` brings specific names into scope. Relative imports use leading dots to navigate within a package. Circular imports — where modules import each other — often signal a design problem and can cause errors at import time. Python searches for modules in `sys.path`, which the `PYTHONPATH` environment variable can extend. The `__all__` attribute defines what names `from module import *` exports, documenting a module's public API. An `__init__.py` marks a directory as a package (namespace packages allow splitting across locations without one in Python 3.3+), and `__main__.py` designates the entry point for `python -m pkg`. Guarding top-level code with `if __name__ == "__main__":` ensures it runs only when the script is invoked directly.

All chapters
  1. 1Python Foundations and Principles
  2. 2Core Data Types and Type Concepts
  3. 3Iteration, Generators, and Comprehensions
  4. 4Functions, Decorators, and Modules
  5. 5Object-Oriented Programming and Dunder Methods
  6. 6Type Hinting and Modern Language Features
  7. 7Concurrency: GIL, Threads, Processes, and Async
  8. 8Standard Library, Tooling, and Ecosystem

Drill it

Reading is not remembering. These come from the Python Deep Dive deck:

Q

What is Python?

A high-level, interpreted, dynamically typed programming language created by Guido van Rossum in 1991.

Q

What is PEP 8?

Python's official style guide.

Q

What is PEP 20?

The Zen of Python — guiding principles by Tim Peters.

Q

What is the difference between Python 2 and Python 3?

Python 3 is the actively maintained version; Python 2 reached end-of-life in 2020.