如何将子模块名称保留在Python包的名称空间之外?

如何将子模块名称保留在Python包的名称空间之外?,python,module,package,Python,Module,Package,我希望某个模块的接口包含一定数量的函数和类(而不是其他)。我可以在一个文件中实现所有这些,并且很容易得到我想要的接口。但是因为有很多代码,我更愿意把整个过程分成几个文件,比如 mypackage/ __init__.py a.py b.py c.py d.py 无论如何,为了获得所需的接口,我为从a、b、c和d导入所有公共符号的包定义了一个\uuuu init\uuuuuuuuuuy.py文件: from a import func_a1, func_a

我希望某个模块的接口包含一定数量的函数和类(而不是其他)。我可以在一个文件中实现所有这些,并且很容易得到我想要的接口。但是因为有很多代码,我更愿意把整个过程分成几个文件,比如

mypackage/
    __init__.py
    a.py
    b.py
    c.py
    d.py
无论如何,为了获得所需的接口,我为从
a
b
c
d
导入所有公共符号的包定义了一个
\uuuu init\uuuuuuuuuuy.py
文件:

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
如果我使用

import mypackage
包名称空间还包含符号
a
b
c
d
。这些名称是实现细节,不是我的接口的一部分。我不希望它们显示为“公共”符号。摆脱它们的最好办法是什么

我考虑的选择是

  • 使用单个模块而不是包。界面看起来会很好,但是实现会比现在更清晰

  • 添加行

    del a, b, c, d
    
    \uuuu init\uuuuuuupy
    的末尾。工作正常,但看起来像一个黑客。(例如,您不能再
    import\uuuu init\uuuu
    ,没有这一行就可以工作了。)

  • a
    b
    c
    d
    重命名为
    \u a
    \u b
    \u c
    \u d
    。现在它们作为“private”符号包含在
    mypackage
    的名称空间中,我对此很满意,但我觉得有点奇怪,我所有的文件名都以下划线开头(实际上,当然有四个子模块以上)

  • 还有更好的建议吗?或者考虑选择哪个选项

    还是我只是在做肛交,不应该关心整件事?

    来自:

    import语句使用 以下约定:如果一个包 __py代码定义了一个名为uuu all_uuuu的列表,它是应该导入的模块名称列表 当从程序包导入时* 遇到

    在您的
    mypackage/\uuuu init\uuuu.py
    中,尝试添加以下内容:

    # 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
    

    如果您真的想从名称空间中删除名称,那么只需对它们使用
    del
    语句,它们就会像风一样消失。

    以下是一个受Javascript单功能模块启发的解决方案:

    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()
    
    虽然模块需要从操作系统导入“path”,但“path”不会污染模块名称空间。模块名称空间中唯一的错误是_init_module(),它用双下划线前缀清楚地标记为private

    另一种选择是在每个函数的顶部导入所需的模块,而不是在模块的顶部导入。第一次导入模块后,后续导入只是sys.modules字典中的查找


    但我同意这里的其他评论者的观点——Python约定并不担心模块名称空间污染,只是让模块的用户清楚地知道名称空间的哪些部分是公共API,哪些是内部文件。

    如果包中的某些文件确实是实现细节,继续,在它们前面加一个下划线——这就是我们使用它们的目的

    例如,如果您查看
    ctypes
    ,您将看到

    __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
    ...
    

    正如您所看到的,甚至
    os
    sys
    都成为了该文件中的实现细节。

    不妨喝杯苏打水。这就是说,除了你之外,没有人真正关心包的名称空间中有什么。@Ignacio:可能你是对的:)例如,在交互使用中,当不需要的名称干扰选项卡扩展时,这让我很恼火。这只是我的意见,但是
    import\uu init\uuuuuu
    在选项2中似乎更为粗俗,所有的事情都考虑过了。但是当直接访问名称空间时,这不会影响名称空间的内容。啊,我误解了你的问题。。。如果您正在执行mypackage import*中的
    ,则此操作有效,我假设您是这样做的。在这种情况下,它似乎不起作用,即使您在模块
    a
    b
    中定义
    \uuuuuuuuuuuuuuuuuuuuuuuuuu
    ,等等中也不起作用。不幸的是,这种方法不起作用。正如您在我的帖子中所看到的,我从不将名称
    a
    导入包的名称空间——我只是使用导入函数a1中的
    。如果在普通模块中执行此操作,则只有名称
    func_a1,…
    会显示在模块的命名空间中。但是由于
    a
    是包的子模块,因此它的名称
    a
    也被插入包的名称空间中。如果我从函数中导入所有内容,也会发生同样的情况。