Python: all
Effective Python Item 50: Use Packages to Organize Modules and Provide Stable APIs
Python can limit the “interface” exposed to API consumers who use import *
by defining the __all__
special attribute of a module or package.
~~~~~ 2017-11-16 补充开始 ~~~~~
Karol Kuczmarski: __all__
and wild imports in Python:
__all__
doesn’t prevent any of the module symbols (functions, classes, etc.) from being directly imported. In our the example, the seemingly omittedbaz
function (which is not included in__all__
), is still perfectly importable by writingfrom module import baz
.
Similarly,__all__
doesn’t influence what symbols are included in the results ofdir(module)
orvars(module)
. So in the case above, adir
call would result in a['Foo', 'bar', 'baz']
list, even though'baz'
does not occur in__all__
.
In other words, the content of__all__
is more of a convention rather than a strict limitation. Regardless of what you put there, every symbol defined in your module will still be accessible from the outside.
This is a clear reflection of the common policy in Python: assume everyone is a consenting adult, and that visibility controls are not necessary.
~~~~~ 2017-11-16 补充结束 ~~~~~
Define __all__
in a module
When consuming code does from foo import *
, only the attributes in foo.__all__
will be imported from foo
. If __all__
isn’t present in foo
, then only public attributes, those without a leading underscore, are imported.
# foo.py
__all__ = ['Foo']
class Foo(object):
pass
Define __all__
for a package
To do this with package mypackage
, you need to modify the __init__.py
file in the mypackage
directory. This file actually becomes the contents of the mypackage
module when it’s imported. Thus, you can specify an explicit API for mypackage
by limiting what you import into __init__.py
.
Suppose mypackage
directory structure is:
mypackage
├── __init__.py
├── model.py
└── util.py
and __all__
are defined in model.py
and util.py
both.
Since all of my internal modules already specify __all__
, I can expose the public interface of mypackage
by simply importing everything from the internal modules and updating __all__
accordingly.
# __init__.py
__all__ = []
from .models import *
__all__ += models.__all__
from .utils import *
__all__ += utils.__all__
Note that from .xxx
(no space between .
and xxx
) means relative import, i.e. importing from a relative path.
PEP 328 – Imports: Multi-Line and Absolute/Relative (Guido’s Decision):
Guido has Pronounced that relative imports will use leading dots. A single leading dot indicates a relative import, starting with the current package. Two or more leading dots give a relative import to the parent(s) of the current package, one level per dot after the first.
Also note that you can also from . import xxx
to relatively import the whole module xxx
.
Comments