I want the interface of some module to contain a certain number of functions and classes (and nothing else). I could implement all of 开发者_开发问答those in a single file, and would easily get the interface I want. But since there is a lot of code, I'd prefer to split the whole thing up into several files, say
mypackage/
__init__.py
a.py
b.py
c.py
d.py
To get the desired interface anyway, I define an __init__.py
file for the package that imports all public symbols from a
, b
, c
and d
:
from a import func_a1, func_a2, ClassA1, ClassA2
from b import func_b1, func_b2, ClassB1, ClassB2
from c import func_c1, func_c2, ClassC1, ClassC2
from d import func_d1, func_d2, ClassD1, ClassD2
If I import the package using
import mypackage
the package namespace also contains the symbols a
, b
, c
and d
. These names are implementation details and not part of my interface. I don't want them to appear as "public" symbols. What is the best way of getting rid of them?
The options I considered are
Use a single module instead of a package. The interface will look fine, but the implementation will get less clear than it is now.
Add the line
del a, b, c, d
to the end of
__init__.py
. Works ok, but seems like a hack. (For example, you can'timport __init__
any more, which works without this line.)Rename
a
,b
,c
andd
to_a
,_b
,_c
and_d
. Now they are included inmypackage
's namespace as "private" symbols, which I'm fine with, but it feels a bit strange that all my filenames start with an underscore (in reality, there are of course more than four submodules).
Any better suggestions? Or thoughts on which option to prefer?
Or am I just to anal and shouldn't care about the whole thing?
If some of the files in a package are indeed implementation details, go ahead and stick an underscore in front of them -- that's what we use them for.
For example, if you look in ctypes
you'll see
__init__.py
==================================================
"""create and manipulate C data types in Python"""
import os as _os, sys as _sys
__version__ = "1.1.0"
from _ctypes import Union, Structure, Array
from _ctypes import _Pointer
from _ctypes import CFuncPtr as _CFuncPtr
...
As you can see, even os
and sys
became implementation details in that file.
If you really want to remove the names from the namespace then you can just use the del
statement on them and they'll disappear like the wind.
Here's a solution inspired by Javascript single-function modules:
def __init__module():
from os import path
def _module_export_1():
return path.abspath('../foo')
def _module_export_2():
return path.relpath('foo/bar', 'foo')
g = globals()
g['module_export_1'] = _module_export_1
g['module_export_2'] = _module_export_2
__init__module()
Although the module needs to import 'path' from os, 'path' doesn't pollute the module namespace. The only cruft in the module namespace is __init_module(), which is clearly marked private by the double-underscore prefix.
Another option would be to import needed modules at the top of each function, rather than the top of your module. After the first time a module is imported, subsequent imports are just a lookup in the sys.modules dictionary.
But I agree with the other commenters here -- the Python convention is not to worry about module namespace pollution, and just make it obvious to your module's users which parts of the namespace are your public API and which are internals.
From http://docs.python.org/tutorial/modules.html:
The import statement uses the following convention: if a package’s __init__.py code defines a list named __all__, it is taken to be the list of module names that should be imported when from package import * is encountered.
In your mypackage/__init__.py
, try adding this:
# add this line, replace "..." with the rest of the definitions
# you want made public
__all__ = ['func_a1', 'func_a2', 'ClassA1', 'ClassA2', ...]
from a import func_a1, func_a2, ClassA1, ClassA2
from b import func_b1, func_b2, ClassB1, ClassB2
from c import func_c1, func_c2, ClassC1, ClassC2
from d import func_d1, func_d2, ClassD1, ClassD2
精彩评论