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

问题描述:

我想要某个模块的接口包含一定数量的函数和类(而不是其他)。我可以在一个文件中实现所有这些,并且很容易获得我想要的接口。但是,由于有大量的代码,我宁愿分裂了整个事情分成几个文件,说如何将子模块名称保留在Python包的名称空间之外?

mypackage/ 
    __init__.py 
    a.py 
    b.py 
    c.py 
    d.py 

如果想获得理想的界面,我定义一个__init__.py文件为导入所有公共包从abcd符号:

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,cd。这些名称是实现细节,不属于我的界面。我不希望它们显示为“公共”符号。什么是摆脱他们的最佳方式?

我考虑的选择是

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

  2. del a, b, c, d 
    

    加入的__init__.py末。工作正常,但看起来像一个黑客。 (例如,你不能import __init__更多,如果没有此项线路正常。)

  3. 重命名abcd_a_b_c_d。现在它们被包含在mypackage的命名空间中作为“私有”符号,我很好,但它感觉有点奇怪,我的文件名以开头, )。

有什么更好的建议吗?或者想要选择哪个选项?

或者我只是为了肛门,不应该关心整个事情?

+2

不妨去喝苏打水。也就是说,除了你以外,没有人真正关心包的命名空间中的内容。 – 2011-03-10 20:29:24

+2

@Ignacio:可能你是对的:)例如在交互式使用中,当不需要的名称会干扰标签扩展时,它会让我感到恼火。 – 2011-03-10 20:37:21

+0

这只是我的看法,但是'import __init__'在选项2中似乎更加骇人听闻,所有事情都考虑到了。 – JAB 2012-06-07 15:09:20

如果你真的要删除从命名空间中的名字,那么你可以只使用他们的del声明,他们会消失,像风。

http://docs.python.org/tutorial/modules.html

import语句使用 以下约定:如果一个包的 __init__.py代码定义了一个名为列表__all__,它被认为是应导入模块的名称列表 当从包导入*时遇到 。

在你mypackage/__init__.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 
+1

但是,这不会直接访问它时影响名称空间的内容。 – 2011-03-10 20:40:15

+0

Gah,我误解了你的问题......如果你正在从'mypackage import *'执行操作,我认为你是这样的。在这种情况下,它似乎不起作用,即使你在你的模块中定义了'__all__',''''''''''' – dcrosta 2011-03-10 20:41:54

这是一个被单一的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() 

虽然模块需要进口从OS“路径”,“路径”不污染模块命名空间。模块名称空间中唯一的残留是__init_module(),它明显由双下划线前缀标记为私有。

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

但我同意其他评论者的意见 - Python约定不担心模块名称空间污染,只是让模块的用户明白哪些部分名称空间是公共API,哪些是内部的。

+0

不幸的是,这种方法不起作用。正如你在我的文章中所看到的,我从来没有将名称'a'导入到包的名称空间中 - 我只是从'import func_a1,...'中使用''。如果我在普通模块中这样做,只会在模块的名称空间中显示名称'func_a1,..'。但是由于'a'是一个包的子模块,它的名称'a'被插入到包的名称空间中。如果我从一个函数中导​​入所有东西,会发生同样的事情。 – 2011-04-04 18:46:08

如果包中的某些文件确实是实现细节,请继续并在它们前面加上下划线 - 这就是我们使用它们的原因。

例如,如果你在​​看你会看到

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

正如你可以看到,即使ossys成了该文件的实施细则。