Jimmy C
Jimmy C

Reputation: 9680

Should I define __all__ even if I prefix hidden functions and variables with underscores in modules?

From the perspective of an external user of the module, are both necessary?

From my understanding, by correctly prefix hidden functions with an underscore it essentially does the same thing as explicitly define __all__, but I keep seeing developers doing both in their code. Why is that?

Upvotes: 2

Views: 362

Answers (3)

Loïc Faure-Lacroix
Loïc Faure-Lacroix

Reputation: 13600

Defining all will overide the default behaviour. There is actually might be one reason to define __all__

When importing a module, you might want that doing from mod import * will import only a minimal amount of things. Even if you prefix everything correctly, there could be reasons not to import everything.

The other problem that I had once was defining a gettext shortcut. The translation function was _ which would not get imported. Even if it is prefixed "in theory" I still wanted it to get exported.

One other reason as stated above is importing a module that imports a lot of thing. As python cannot make the difference between symbols created by imports and the one defined in the actual module. It will automatically reexport everything that can be reexported. For that reason, it could be wise to explicitely limit the thing exported to the things you want to export.

With that in mind, you might want to have some prefixed symbols exported by default. Usually, you don't redefine __all__. Whenever you need it to do something unusual then it may make sense to do it.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1124000

When importing from a module with from modulename import * names starting with underscores are indeed skipped.

However, a module rarely contains only public API objects. Usually you've made imports to support the code as well, and those names are global in the module as well. Without __all__, those names would be part of the import too.

In other words, unless you want to 'export' os in the following example you should use __all__:

import os
from .implementation import some_other_api_call

_module_path = os.path.dirname(os.path.abspath(__file__))
_template = open(os.path.join(_module_path, 'templates/foo_template.txt')).read()

VERSION = '1.0.0'

def make_bar(baz, ham, spam):
    return _template.format(baz, ham, spam)

__all__ = ['some_other_api_call', 'make_bar']

because without the __all__ list, Python cannot distinguish between some_other_api_call and os here and divine which one should not be imported when using from ... import *.

You could work around this by renaming all your imports, so import os as _os, but that'd just make your code less readable.

And an explicit export list is always nice. Explicit is better than implicit, as the Zen of Python tells you.

Upvotes: 7

Tim Peters
Tim Peters

Reputation: 70715

I also use __all__: that explictly tells module users what you intend to export. Searching the module for names is tedious, even if you are careful to do, e.g., import os as _os, etc. A wise man once wrote "explicit is better than implicit" ;-)

Upvotes: 1

Related Questions