Reputation: 54068
I'm writing a small package in Python with the following module structure
package/
__init__.py
cls1.py
cls2.py
cls3.py
mod1.py
mod2.py
Where each clsN.py
has a single class ClsN
in it, and each modN.py
contains various functions using those classes. I would like my import structure to look like
package
package.Cls1
package.Cls2
package.Cls3
package.mod1
package.mod1.func1
...
package.mod2
package.mod2.func2
...
without polluting the namespace. Right now I have the problem that I can access any of these classes through package.Cls1
and package.cls1.Cls1
, and I can also access everything defined in package/cls1.py
(including imports). I've tried setting the __all__
variable, but that appears to do nothing except during from package import *
. Is there a way to keep Python from showing clsN
as submodules? Or even just a way to hide imports inside those files? I don't want the users of my library to have two ways of getting to each class.
Upvotes: 2
Views: 2016
Reputation: 365915
The stdlib has two ways of dealing with cases like this.
The first is simple: Just mark the private modules private in the same way you mark any other attributes private: with an underscore prefix. This doesn't hide them from users, but it does signify that users shouldn't use them (and may prevent things like iPython or PyDev from offering them as completions), and that's usually good enough.
The only real problem with this is that underscore prefixes on module names also tend to imply that they're native accelerators—in other words, a user might expect that _foo
is implemented by foomodule.c
rather than _foo.py
.
Another alternative is to either not import them—use from submodule import name, other, yetanother
—or to del
them at the end of your module. The fact that you explicitly want package.Cls1
to be the same type as package.cls1.Cls1
, etc. implies that this is exactly what you want.
The user can still explicitly import them if they want. But Python doesn't generally give you any way to block malice or intentional stupidity, and you shouldn't try.
Upvotes: 4
Reputation: 104762
First off, I suggest not worrying too much about what is visible from where. If a user tries to access package.mod1.Cls1
, is it really a problem if it works? Python is generally a pretty permissive language, and trying to hide stuff is often more trouble than it's worth.
That said, if you want to make non-standard access a bit less likely, a good way is to import your classes into your modules with alternative names, starting with underscores. Variables with single underscores at the start of their names are not imported with from _ import *
nor do they show up in a dir
listing. Here's how you could do that:
In mod1.py
:
from .Cls1 import Cls1 as _Cls1
from .Cls2 import Cls2 as _Cls2
from .Cls3 import Cls3 as _Cls3
Then just use the underscore names in your module's code. Other code that wants to get at the classes will probably go find them under their "official" location.
Upvotes: 2