4

In Python convention, should a module's imports be considered part of its public interface, or not?

I have some code that does this:

foo.py:

from a import b

bar.py:

from foo import b

I'm trying to decide whether to refactor bar.py to import b directly from a. I suppose there may be some cases where you want foo.py to control the implementation of b that bar.py uses. But if that's not the case, wouldn't it be better practice to have both modules import it the same way?

joeforker
  • 40,459
  • 37
  • 151
  • 246
John B
  • 3,391
  • 5
  • 33
  • 29
  • if you want to make them private you could always do `import b as _b`. Also this question seems a bit opinion based, if you can think of a way to rephrase it to be more objective that would prevent this from being closed as off topic. – Tadhg McDonald-Jensen Jun 13 '16 at 19:04

4 Answers4

2

In Python convention, should a module's imports be considered part of it's public interface, or not?

A module's public API is whatever the module documents its public API to be. If module foo documents that it provides b, then b is part of its public API, whether b is actually defined in foo or imported from elsewhere.

Many modules split their code into multiple files and import all the pieces together into a single module. For example, the collections module puts part of its code in a C _collections module and does

from _collections import deque, defaultdict

deque and defaultdict are unambiguously part of collections's public API.

If a module imports something that isn't supposed to be part of its public API, it's often a good idea to do import thing as _thing, putting an underscore on it to signal that the imported thing is an implementation detail.

user2357112
  • 260,549
  • 28
  • 431
  • 505
2

Documentation point of view. The convention is that everything that does not start with underscore is public. If you want to mark an import symbol as private, alias it with underscore like from a import b as _b. There's an alternative. You can use __all__ instead, to declare the symbols that you consider public. Even though technically it only affects from module import *, it is also a good declaration of intent.

Dependency point of view. I wouldn't recommend complicating dependency graph without strong reason to do so, because otherwise there're higher chances to stumble over circular import issues later in development process.

According to Zen of Python, explicit is better than implicit and simple is better than complex. So if module a provides symbol b, it is reasonable that all modules that use b import it from a.

Community
  • 1
  • 1
saaj
  • 23,253
  • 3
  • 104
  • 105
2

PEP 8 gives some guidance here.

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.

KSR
  • 221
  • 2
  • 4
1

ninja mode module definition

def defmod():
    global foo
    import sys
    def foo(): return sys.path
defmod()
del defmod
joeforker
  • 40,459
  • 37
  • 151
  • 246