Data Hiding in Modules

As we’ve seen, a Python module exports all the names assigned at the top level of its file. There is no notion of declaring which names should and shouldn’t be visible outside the module. In fact, there’s no way to prevent a client from changing names inside a module if it wants to.

In Python, data hiding in modules is a convention, not a syntactical constraint. If you want to break a module by trashing its names, you can, but fortunately, I’ve yet to meet a programmer for whom this was a life goal. Some purists object to this liberal attitude toward data hiding, claiming that it means Python can’t implement encapsulation. However, encapsulation in Python is more about packaging than about restricting. We’ll expand this idea in the next part in relation to classes, which also have no privacy syntax but can often emulate its effect in code.

Minimizing from * Damage: _X and __all__

As a special case, you can prefix names with a single underscore (e.g., _X) to prevent them from being copied out when a client imports a module’s names with a from * statement. This really is intended only to minimize namespace pollution; because from * copies out all names, the importer may get more than it’s bargained for (including names that overwrite names in the importer). Underscores aren’t “private” declarations: you can still see and change such names with other import forms, such as the import statement:

# unders.py
a, _b, c, _d = 1, 2, 3, 4

>>> from unders import *                 # Load non _X names only
>>> a, c
(1, 3)
>>> _b
NameError: name '_b' is not defined

>>> import unders                        # But other importers get every name
>>> unders._b
2

Alternatively, you can achieve a hiding effect similar to the _X naming convention by assigning a list of variable name strings to the variable __all__ at the top level of the module. When this feature is used, the from * statement will copy out only those names listed in the __all__ list. In effect, this is the converse of the _X convention: __all__ identifies names to be copied, while _X identifies names not to be copied. Python looks for an __all__ list in the module first and copies its names irrespective of any underscores; if __all__ is not defined, from * copies all names without a single leading underscore:

# alls.py
__all__ = ['a', '_c']                    # __all__ has precedence over _X
a, b, _c, _d = 1, 2, 3, 4

>>> from alls import *                   # Load __all__ names only
>>> a, _c
(1, 3)
>>> b
NameError: name 'b' is not defined

>>> from alls import a, b, _c, _d        # But other importers get every name
>>> a, b, _c, _d
(1, 2, 3, 4)

>>> import alls
>>> alls.a, alls.b, alls._c, alls._d
(1, 2, 3, 4)

Like the _X convention, the __all__ list has meaning only to the from * statement form and does not amount to a privacy declaration: other import statements can still access all names, as the last two tests show. Still, module writers can use either technique to implement modules that are well behaved when used with from *. See also the discussion of __all__ lists in package __init__.py files in Chapter 24; there, these lists declare submodules to be automatically loaded for a from * on their container.