Elad Weiss
Elad Weiss

Reputation: 4002

What is the preferred way to export python symbols from a pacakge?

Let's say I have the following structure:

And the user of the package wants to use class1 and class2.

The way I understand I have too options, in my __init__.py:

from module1 import class1,class2

Which will let the user do something like x = pacakge.class1()

Or:

import module1

Which will let the user do something like x = pacakge.module1.class1()

Is there is a preferred way?

Upvotes: 0

Views: 580

Answers (2)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

It really depends on the context - your package's, modules, classes, functions etc semantics, what they are used for, how they relate to one another etc, and of course the size of your package (how many subpackages / modules, how many names in each etc) - so there's no one-size-fits-all technical answer here.

If your modules are only an implementation detail (ie to avoid having one monolithic 10+k source file) and from a semantic POV some object (function, class, whatever) really belongs to the package's top-level, then use the package's __init__ as a facade so the user don't have to know where the name is effectively defined.

If your modules/subpackages make sense as coherent namespaces (like for example django.db and django.forms) then you obviously want to keep them clearly distinct and let the user explicitely import from them.

Also keep in mind that once a submodule or subpackage name is publicly exposed (it's existence is documented and client code imports from it) then it is de-facto part of the API and you cannot rename it / move it / move things from it without breaking client code.

NB in this last case you can still turn a module into a package used as a facade (using the packages __init__) but the change must (well, should) be transparent to the client code.

As a general rule: start with the simplest implementation - either a plain single module if it's a small lib (only a few public names exposed) that doesn't require namespacing or a package with a few "namespace" submodules for bigger or less cohesive libs, and if your modules grow to huge turn them into subpackages using the package's __init__ to hide the fact to client code.

Upvotes: 1

Elad Weiss
Elad Weiss

Reputation: 4002

One disadvantage of the 2nd approach:

  • If the package has many modules, the user will have to do something like:

    from package.module1 import *
    from package.module2 import *
    ...
    

    Which can be a little annoying.

So, here is what I think would work best:

  1. I structure my package to subpackages based on context.
  2. Main package will import subpackages.
  3. Subpackage will import all symbols from all of its modules.

This way, if my user wants to use the pacakage's foo features, he can simply do from pacakge.foo import * and get foo and only foo features.

Upvotes: 0

Related Questions