首页 > 解决方案 > 使用 __all__ 传播模块导入到顶部

问题描述

我想将一些模块(utils)传播到顶部,但也控制使用传播的内容__all__

例如:

有一个包all,其中的内容__init__被导入alpha_utils,所以从用户的角度来看,这些方法就像来自alpha_utils.

包也alpha_utils由模块导入top_utils_module

我想要做:

top_utils_module.beta_fn(即使beta_fnall包装中) top_utils_module.alpha_fn(即使betaalpha_utils包装中)

我可以使用 alwaysimport from <some_module_name> all但我想通过使用来控制每个模块公开的内容__all__

使用下面的结构是行不通的,我可以得到我想要的东西,但不能降低两层。

-- top_2 utils_module

...
+-- alpha_utils
|   -- __init__
|   -- alpha_module_1
|   -- alpha_module_2
|   -- alpha_module_3

....
+-- all
|   -- __init__
|   -- all_module_1
|   -- all_module_2
|   -- all_module_3

all文件夹中__init__

from .all_module_1 import fun1, fun12
from .all_module_2 import fun2
from .all_module_3 import fun3
__all__ = ["fun1", "fun2", "fun12", "fun2", "fun3"] 

alpha_utils文件夹中__init__

from ..all import *
_all__ = ["alpha_module_1", "alpha_module_2", "alpha_module_3"] ## ? how to integrate ..all

top_2 utils_module

from some.tir import tir
from alpha_utils import *

标签: pythonpython-import

解决方案


问题

回顾和简化,因为这个问题有点吵,很难理解。有3个模块/包。

术语表:import packagemoduleto export__all__我的意思是通过在模块级属性中定义一系列符号名称来传达模块/包的公共 API 的范围。

  • a-- 我们希望最终用户从中使用 API 的顶级模块;它从b
  • b-- 导出自己的模块的导入包,但也重新导出导入包中的符号c
  • c-- 从其模块中导出单个符号的导入包

b根模块应该是__init__.py什么样子?

一个解法

从一个角度来看,这是一种解决方法,解决方案是更改需求并a直接从c. 但从另一个角度来看,根据您对代码所有权和团队责任的引用,它可能正是您想要的。

我的示例导入包如下所示。

├── a.py
├── b
│   ├── bm1.py
│   ├── bm2.py
│   └── __init__.py
└── c
    ├── _cm1.py
    ├── _cm2.py
    └── __init__.py
  • c/__init__.py

    因为c我假设它的模块是内部的,因为它从中导出特定的功能,因此它们以下划线开头。

    from ._cm1 import cm1_fn1, cm1_fn2
    from ._cm2 import cm2_fn1, cm2_fn2
    
    __all__ = 'cm1_fn1', 'cm1_fn2', 'cm2_fn1', 'cm2_fn2'
    
  • c/_cm1.py

    def cm1_fn1(): pass
    def cm1_fn2(): pass
    
  • c/_cm2.py

    def cm2_fn1(): pass
    def cm2_fn2(): pass
    
  • b/__init__.py

    因为b我假设它的模块是公共的并且有自己的公共 API。

    请注意,修改globals的 将来会有一些名称冲突的风险。此外,一些代码分析工具可能无法理解修改。

    from . import bm1, bm2
    from .. import c
    
    __all__ = ('bm1', 'bm2') + c.__all__
    
    _own_scope = globals()
    for name in c.__all__:
        _own_scope[name] = getattr(c, name)
    
  • b/bm1.py

    __all__ = 'bm1_fn1',
    
    def bm1_fn1(): pass
    
  • b/bm2.py

    __all__ = 'bm2_fn1',
    
    def bm2_fn1(): pass
    
  • a.py

    from .b import *
    from .b import __all__
    

有了这个,我可以跳出目录,说 namedso66939804并运行以下python3REPL。

>>> from so66939804 import a
>>> a.__all__
('bm1', 'bm2', 'cm1_fn1', 'cm1_fn2', 'cm2_fn1', 'cm2_fn2')
>>> dir(a)
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'bm1', 'bm2', 'cm1_fn1', 'cm1_fn2', 'cm2_fn1', 'cm2_fn2']

推荐阅读