views:

108

answers:

4

I have a package mypack with modules mod_a and mod_b in it. I intend the first two 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 this -- 'private' modules -- an accepted practice in Python programming?

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?

A: 

Don't import mod_b into your mypack/__init__.py file. This will make accessing mod_b less than trivial to import in end-user code.

Matt Joiner
Matt, do you mean mod_b instead of mod_a, because the former is the one I want to keep private? Assuming you do, isn't the statement 'import mypack.mod_b' trivial enough (because, if I'm not wrong, that's all the end-user has to type)? What exactly then you mean by 'less than trivial'?
Frederick
By less than trivial I mean it's hopeless to assume that you can make it inaccessible. By not importing it into your `__init__.py`, you at least make it inaccessible through the import `mypack.mod_b` route, the most obvious one. This should more than suffice to make it "private".
Matt Joiner
But Matt, even if `__init__.py` doesn't import `mod_b`, a client can still successfully run `mypack.mod_b`. That means not importing doesn't in any way hinder the use of `mod_b`. I'm surely missing something here. Could you please clarify?
Frederick
+1  A: 

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.

Ivo van der Wijk
I could leave `mod_b` accessible just like `mod_a`. However, imagine there are 20 modules akin to `mod_b`. So when the client types `mypack.` in the editor, the intellisense will list all 25 or so private as well as public modules. This will make my code harder to explore. That's the reason I want to 'hide' those modules which are not meant for the client. As for documentation, people generally prefer exploring to reading documentation.
Frederick
As Matt Joiner said, don't import `mod_b` into your `__init__.py`. I suggest you also look up the `__all__` special variable. If neither of those gets the job done, the problem is in Intellisense.
ssokolow
ssokolow, see the third comment under Matt's answer. Even when `mod_b` is not imported in `mypack/__init__.py`, typing `import mypack.mod_b` in the client code still works successfully. That's because Python doesn't require importing of nested modules into top level ones for "dot" imports (like `mypack.mod_b`) to work.
Frederick
+1  A: 

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.

aterrel
Aterrel, what do you mean by "don't include in `__init__.py`"? Do mean don't place the statement `import mod_b` in `__init__.py`? If so, that still doesn't solve my problem. The client can still import `mod_b` just like a public module: `import mypack.mod_b`. Or do you mean something else?
Frederick
So it won't strictly enforce the module being private, just as a single underscore is weak. But it doesn't show up without explicitly importing it, which is still pretty private. I imagine you could name the file with two leading underscores, but I haven't ever played with this.
aterrel
Thanks for the response aterrel. The solution I'm settling on is creating a subpackage `private` under `mypack` and placing all modules I want to 'hide' in that. This serves my purpose of making my code easier to explore (through intellisense, for example), by tucking away all the uneeded fluff.
Frederick
+1  A: 

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.

Frederick