lzc2016
lzc2016

Reputation: 21

Why do packages have imports under __init__.py file?

For example in multiprocessing package we can import class Process using from multiprocessing import Process. Why not from multiprocessing.context import Process where it really belongs?

In fact, I found that they are the same. Why?

Upvotes: 2

Views: 882

Answers (2)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250951

Adding imports to __init__ is usually done to shorten the import paths and define a public interface. multiprocessing.context import Process is an internal interface and can change in future without maintaining any backwards compatibility.

On the other hand multiprocessing import Process is the documented public interface and won't change to break backwards compatibility.

You could see that __all__ under context.py is empty meaning it has no public interface and you should not import from it in your application as it can change in future without any warnings.

__all__ = []            # things are copied from here to __init__.py

Related section on this from PEP-008:

Public and internal interfaces

Any backwards compatibility guarantees apply only to public interfaces. Accordingly, it is important that users be able to clearly distinguish between public and internal interfaces.

Documented interfaces are considered public, unless the documentation explicitly declares them to be provisional or internal interfaces exempt from the usual backwards compatibility guarantees. All undocumented interfaces should be assumed to be internal.

To better support introspection, modules should explicitly declare the names in their public API using the __all__ attribute. Setting __all__ to an empty list indicates that the module has no public API.

Even with __all__ set appropriately, internal interfaces (packages, modules, classes, functions, attributes or other names) should still be prefixed with a single leading underscore.

An interface is also considered internal if any containing namespace (package, module or class) is considered internal.

Imported names should always be considered an implementation detail. Other modules must not rely on indirect access to such imported names unless they are an explicitly documented part of the containing module's API, such as os.path or a package's __init__ module that exposes functionality from submodules.


The famous requests library has a really nice public interface in my opinion and you could see that it is being done by importing most of things in the __init__.py file. And you're going to find that it is also documented based on the imports under __init__.py file.

Upvotes: 2

Cory Madden
Cory Madden

Reputation: 5193

from multiprocessing import Process works because Process is imported into the __init__.py of the multiprocessing package. For example, in your shell, type the following code:

import multiprocessing
with open(multiprocessing.__file__, 'r') as f:
    print(f.readlines())

You'll see the lines:

from . import context

#
# Copy stuff from default context
#

globals().update((name, getattr(context._default_context, name))
                 for name in context._default_context.__all__)
__all__ = context._default_context.__all__

So yes, it's the same thing.

Upvotes: 1

Related Questions