简体   繁体   中英

How do I prevent users from importing x from a submodule when it exposed in the parent using __all__

Consider the following scenario:

  • You have a module M defined in m.py containing a function f .

    It can be called like this:

     import M; Mf() 
  • The module grows to a size where it is impractical to have in a single file. You split M up into submodules MX , MY , MZ and put the following in M/__init__.py :

     from .X import * from .Y import * from .Z import * __all__ = ["f"] 

    The original code still works:

     import M; Mf() 

However, new consumers of the code may mistakenly access the submodule directly:

import M.X;
M.X.f()

I would like to prevent this, such that all code is still consistently addressing M directly, and not any of the submodules.

The submodules are for the benefit of internal code organisation, and referencing M leaves the possibility of easy reorganisation in the future.

One option would be to name the submodules _X , _Y , and _Z to communicate that they are internal. Is that the recommended approach?

One option would be to name the submodules _X, _Y, and _Z to communicate that they are internal. Is that the recommended approach?

As you don't want people to access MX, you will need to move the X.py module so it is no longer available as MX You could delete it as Kaie suggests, but ugh. Therefore yes, your suggestion is the recommended approach.

  1. Move M/X.py to M/_X.py

  2. In M/__init__.py have the line from ._X import f

As others have suggested, it shouldn't really be a problem that people can access the code and it's habits of programming in a language with stronger encapsulation seeping into your Python designs.

The submodules are for the benefit of internal code organisation, and referencing M leaves the possibility of easy reorganisation in the future.

Yes this is a concern I had when coming from C and C++ and dealing with ABIs all day. But it's often not an issue if the code is small and well tested enough. And this is the kind of thing that you can trivially fix later. If you one day decide to reorganize the code to have X as _X then I'm sure Jenkins can tell you what else needs to be updated. :)

There is a way, but I don't think you gonna like it:

# M/__init__.py

from .X import *
del X
__all__ = ["x"]

You can still do import MX , but will throw Error when using it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM