In Python, the __all__ attribute is a crucial mechanism for defining the public interface of a module. It acts as an explicit contract, specifying which names should be considered “public” and therefore exported when a client uses a from module import * statement. This mechanism provides control and clarity, preventing the accidental exposure of internal implementation details and guiding users toward the intended API.

The Purpose and Mechanics of __all__

When a module does not define __all__, the import * statement imports all names that do not begin with an underscore (_). This default behavior is often undesirable, as it can clutter the importing namespace with helper functions, classes, and variables meant only for internal use. By defining __all__, the module author takes explicit control, dictating precisely which names are part of the stable, public API.

The __all__ attribute must be a sequence of strings, typically a list, defined at the top level of the module. Each string must exactly match the name of a global variable, function, class, or other object defined in or imported into the module.

# my_module.py

__all__ = ['public_function', 'PublicClass']

def public_function():
    """This is part of the public API."""
    return "Hello from public_function"

def _private_helper():
    """This is an internal function and is not exported by import *."""
    return "Internal calculation"

class PublicClass:
    """This class is explicitly exported."""

class _InternalClass:
    """This class is considered private."""

When a user runs from my_module import *, only public_function and PublicClass will be brought into their namespace. _private_helper and _InternalClass will remain hidden, even though they don’t start with an underscore in the __all__ list.

Interaction with Other Import Forms

It is vital to understand that __all__ only affects the wildcard import * statement. It does not restrict access in any other way. A user can still explicitly import a “private” name using from my_module import _private_helper or access it via import my_module; my_module._private_helper(). The purpose of __all__ is not to enforce security or true privacy but to signal intent and clean up the namespace during wildcard imports. The underscore prefix is the conventional marker for “internal use,” and __all__ works in concert with this convention.

Best Practices and Common Pitfalls

A best practice is to always define __all__ in modules that constitute a public API. This makes your intentions clear to users and future maintainers. The list should be ordered, often alphabetically, to make it easy to read and maintain.

One of the most common pitfalls is an inaccurate __all__ list. If a name is included in __all__ but is not defined in the module, an AttributeError will be raised during a wildcard import.

# problematic_module.py

__all__ = ['exists', 'does_not_exist']  # 'does_not_exist' is a typo or was removed

def exists():
    pass

# User runs: from problematic_module import *
# AttributeError: module 'problematic_module' has no attribute 'does_not_exist'

To avoid this, many integrated development environments (IDEs) and linters can be configured to validate the names in __all__ against the module’s actual contents. Another pitfall involves dynamically generated names. Since __all__ is a static list, it cannot easily contain names created at runtime, which should generally be avoided in a public API anyway.

Dynamic and Conditional Modification of __all__

While __all__ is typically a static list, it is possible to construct it dynamically. This can be useful for exporting a set of names that share a common pattern or for conditionally including names based on the runtime environment (e.g., operating system).

# dynamic_export.py

import sys

# Collect all functions that start with 'export_'
_names = [name for name in globals() if name.startswith('export_')]
# ... or conditionally include based on platform
if sys.platform == "win32":
    _names.append('windows_specific_function')

__all__ = _names

def export_utility():
    pass

def internal_function():
    pass

def windows_specific_function():
    pass

However, this practice should be used sparingly, as it can make the module’s API less obvious and harder to reason about for someone reading the source code. Static lists are almost always preferable for their simplicity and predictability.