如何将子模块名称保留在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
的名称空间中,我对此很满意,但我觉得有点奇怪,我所有的文件名都以下划线开头(实际上,当然有四个子模块以上)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
也被插入包的名称空间中。如果我从函数中导入所有内容,也会发生同样的情况。