What does the __all__ attribute mean in Python, and what is its purpose in __init__.py files?
The __all__ attribute in Python is a list of strings that explicitly defines which names should be considered part of a module’s public API. When used in __init__.py files, it controls what gets imported when a user uses from package import * statements, allowing developers to define the public interface of their packages and prevent private implementation details from being exposed.
Contents
- What is all?
- How all Controls Wildcard Imports
- all in init.py Files
- Best Practices for Using all
- Examples and Implementation
What is all?
In Python, the __all__ attribute is a special variable that exists in modules to specify which names should be considered part of the module’s public interface. When defined, it should contain a list of strings representing the names that should be available when someone uses a wildcard import statement like from module import *.
According to the official Python documentation, __all__ serves as an explicit declaration of a module’s public API. This is particularly important because Python traditionally uses the convention of names starting with an underscore (_) to indicate private or internal implementation details that shouldn’t be part of the public interface.
When __all__ is not defined in a module, wildcard imports will import all names that don’t start with an underscore. However, when __all__ is present, only the names listed in it will be imported, regardless of their naming convention. This gives developers precise control over what constitutes their public API.
How all Controls Wildcard Imports
The primary purpose of __all__ is to control what gets imported when wildcard import statements are used. This behavior is crucial for maintaining clean, predictable module interfaces and preventing accidental exposure of internal implementation details.
As explained by GeeksforGeeks, __all__ defines “what names should be imported from the current module (Python file), to another file. Only the variables that are declared in that list can be used in that other file after importing this.”
Here’s how it works:
-
Without
__all__: When a module doesn’t define__all__, wildcard imports (from module import *) will import all names that don’t start with an underscore. -
With
__all__: When a module defines__all__, only the names explicitly listed in the__all__list will be imported, regardless of their naming convention.
This behavior is demonstrated in the following example:
# Without __all__
def _internal_function():
pass
def public_function():
pass
class _InternalClass:
pass
class PublicClass:
pass
# Wildcard import would import: public_function, PublicClass
# With __all__
__all__ = ['public_function', 'PublicClass']
def _internal_function():
pass
def public_function():
pass
class _InternalClass:
pass
class PublicClass:
pass
# Wildcard import would import only: public_function, PublicClass
The __all__ attribute essentially acts as a whitelist that overrides Python’s default behavior of importing all non-underscored names.
all in init.py Files
In Python packages, the __init__.py file serves as the initialization script that gets executed when the package is imported. When __all__ is used in __init__.py files, it plays a crucial role in defining the package’s public API and controlling how submodules are exposed.
According to Real Python, the __init__.py file is “an acceptable place to put the public API or a package, with the other modules within it providing the implementation.” The __all__ attribute in this context helps developers create a clean, organized package interface.
Key Purposes of all in init.py:
-
Package-level API Definition: When you define
__all__in a package’s__init__.py, you’re specifying which modules, classes, and functions should be considered part of the package’s public interface. -
Wildcard Import Control: Just like in regular modules,
__all__controls what gets imported when someone usesfrom package import *. -
Namespace Organization: It helps organize the package’s namespace by explicitly listing what should be available at the package level.
A common pattern is to use __all__ in __init__.py combined with importing from submodules:
# package/__init__.py
"""
Package docstring explaining the package's purpose.
"""
__all__ = ['module1', 'module2', 'SomeClass']
from . import module1
from . import module2
from .module3 import SomeClass
This approach allows users to import from the package directly (from package import SomeClass) while keeping the implementation details in separate modules.
As noted in Python.org discussions, some projects make use of the __all__ member in the __init__.py file to “declare members that are part of the public API of a package.”
Best Practices for Using all
Using __all__ effectively requires understanding Python’s conventions and following established best practices. Here are the key recommendations from the research:
1. Explicitly Define Your Public API
The most important practice is to explicitly define your public API using __all__. As Real Python explains, “the all variable can help explicitly define a module’s public interface.”
2. Follow Naming Conventions
Python uses underscore prefixes (_) to indicate private names. According to Real Python, “In contrast, if a name starts with a lowercase or uppercase letter, then that name is public and, therefore, is part of the module’s public API.” However, when you define __all__, you can override this convention and explicitly include private-named functions or classes in your public API.
3. Be Selective About What You Export
Only include in __all__ what you actually want users to use. As noted in Reddit discussions, “putting the names of what you want to keep ‘public’ in all tells Python what to import with star imports.”
4. Use all for Documentation
The __all__ list serves as documentation of your module’s public API. It helps other developers understand what they’re supposed to use and what should be considered implementation details.
5. Avoid Overusing Wildcard Imports
While __all__ helps control wildcard imports, many Python developers recommend avoiding import * statements altogether because they can make code harder to read and debug. As mentioned in Stack Overflow, “Be aware though that using import * in code is generally very bad practice and should be avoided if possible.”
Examples and Implementation
Let’s explore some practical examples of how __all__ is implemented in different scenarios.
Basic Module Example
Here’s a simple module showing how __all__ controls what gets imported:
# mymodule.py
"""
A sample module demonstrating __all__ usage.
"""
__all__ = ['public_function', 'PublicClass']
def _private_function():
"""This is a private function that won't be imported."""
return "Private"
def public_function():
"""This is a public function that will be imported."""
return "Public"
class _PrivateClass:
"""This is a private class that won't be imported."""
pass
class PublicClass:
"""This is a public class that will be imported."""
pass
When someone uses from mymodule import *, only public_function and PublicClass will be available in the namespace.
Package Example with init.py
Here’s how __all__ is commonly used in package __init__.py files:
# mypackage/__init__.py
"""
A sample package demonstrating __all__ usage in __init__.py.
"""
__all__ = ['Database', 'connect', 'disconnect']
from .database import Database
from .connection import connect, disconnect
# Package-level initialization code
def initialize():
"""Initialize the package."""
pass
And the corresponding modules:
# mypackage/database.py
"""
Database functionality.
"""
class Database:
"""Main database class."""
def __init__(self, name):
self.name = name
# mypackage/connection.py
"""
Connection functionality.
"""
def connect(db_url):
"""Connect to a database."""
pass
def disconnect():
"""Disconnect from a database."""
pass
This setup allows users to import directly from the package:
from mypackage import Database, connect, disconnect
Advanced Example with Conditional Exports
Sometimes you might want to conditionally export different items based on the Python version or available dependencies:
# advanced_module.py
"""
Advanced module with conditional exports.
"""
try:
import numpy as np
has_numpy = True
except ImportError:
has_numpy = False
__all__ = ['basic_function', 'DataProcessor']
if has_numpy:
__all__.append('numpy_function')
def basic_function():
"""A basic function always available."""
return "Basic functionality"
def numpy_function():
"""A function that requires numpy."""
return np.array([1, 2, 3])
class DataProcessor:
"""A data processing class."""
def process(self, data):
return [x * 2 for x in data]
This pattern allows the module to gracefully handle missing dependencies while still providing a clean public API.
Conclusion
The __all__ attribute in Python is a powerful tool for defining explicit public APIs and controlling what gets exported from modules and packages. Its key benefits include:
- Explicit API Definition: Provides clear documentation of what constitutes a module’s public interface
- Namespace Control: Prevents accidental exposure of private implementation details
- Package Organization: Helps structure clean, predictable package hierarchies
- Import Management: Controls wildcard import behavior when
from module import *is used
When used in __init__.py files, __all__ becomes particularly valuable for package developers who want to create intuitive public interfaces while keeping implementation details organized in separate modules. By following best practices like being selective about exports, respecting naming conventions, and avoiding overuse of wildcard imports, developers can leverage __all__ to create more maintainable and user-friendly Python packages.
Sources
- What does all mean in Python? - Stack Overflow
- Python all - GeeksforGeeks
- Usage of all in init.py - Python.org Discussions
- What Is Python’s init.py For? – Real Python
- Python’s all: Packages, Modules, and Wildcard Imports – Real Python
- How do I write good/correct package init.py files - Stack Overflow
- API of a package in python. In init.py? - Stack Overflow
- To Code or Not to Code in init.py: Weighing the Pros and Cons - Medium
- How to organize Python API module to make it neat? - Stack Overflow
- What should you write into the init.py file? - YouTube