Python: 'Private' module in a package

Question:

I have a package mypack with modules mod_a and mod_b in it. I intend the package itself and mod_a to be imported freely:

import mypack
import mypack.mod_a

However, I’d like to keep mod_b for the exclusive use of mypack. That’s because it exists merely to organize the latter’s internal code.

My first question is, is it an accepted practice in Python programming to have ‘private’ modules like this?

If yes, my second question is, what is the best way to convey this intention to the client? Do I prefix the name with an underscore (i.e. _mod_b)? Or would it be a good idea to declare a sub-package private and place all such modules there?

Asked By: Frederick The Fool

||

Answers:

Python doesn’t strictly know or support “private” or “protected” methods or classes. There’s a convention that methods prefixed with a single underscore aren’t part of an official API, but I wouldn’t do this on classes or files – it’s ugly.

If someone really needs to subclass or access mod_b, why prevent him/her from doing so? You can always supply a preferred API in your documentation and document in your module that you shouldn’t access it directly and use mypack in stead.

Answered By: Ivo van der Wijk

While there are not explicit private keywords there is a convention to have put private functions start with a single underscore but a double leading underscore will make it so others cannot easily call the function from outside the module. See the following from PEP 8

- _single_leading_underscore: weak "internal use" indicator.  E.g. "from M
  import *" does not import objects whose name starts with an underscore.

- single_trailing_underscore_: used by convention to avoid conflicts with
  Python keyword, e.g.

  Tkinter.Toplevel(master, class_='ClassName')

- __double_leading_underscore: when naming a class attribute, invokes name
  mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

- __double_leading_and_trailing_underscore__: "magic" objects or
  attributes that live in user-controlled namespaces.  E.g. __init__,
  __import__ or __file__.  Never invent such names; only use them
  as documented.

To make an entire module private, don’t include it __init__.py file.

Answered By: aterrel

The solution I’ve settled on is to create a sub-package ‘private’ and place all the modules I wish to hide in there. This way they stay stowed away, leaving mypack‘s module list cleaner and easier to parse.

To me, this doesn’t look unpythonic either.

Answered By: Frederick The Fool

I prefix private modules with an underscore to communicate the intent to the user. In your case, this would be mypack._mod_b

This is in the same spirit (but not completely analogous to) the PEP8 recommendation to name C-extension modules with a leading underscore when it’s wrapped by a Python module; i.e., _socket and socket.

Answered By: Jeremy

One thing to be aware of in this scenario is indirect imports. If in mypack you

from mypack._mod_b import foo
foo()

Then a user can

from mypack import foo
foo()

and be none the wiser. I recommend importing as

from mypack import _mod_b
_mod_b.foo()

then a user will immediately see a red flag when they try to

from mypack import _mod_b

As for actual directory structure, you could even extend Jeremy’s answer into a _package_of_this_kind package, where anything in that can have any ‘access modifiers’ on it you like – users will know there be dragons

Answered By: joel
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.