繁体   English   中英

从我的 Python 模块导入另一个模块

[英]Making another module importable from my Python module

我有一个像这样的 Python 模块目录结构:

my_module
|--__init__.py
|--public_interface
|  |--__init__.py
|  |--my_sub_module
|  |  |--__init__.py
|  |  |--code.py
|  |--some_more_code.py
|--other directories omitted

现在, public_interface目录(以及其他几个目录)仅用于将代码组织成逻辑子单元,作为我和其他开发人员的指南。 my_module的最终用户只能将其视为my_module.my_sub_module ,中间没有public_interface

我写了这些__init__.py文件:

my_module.__init__.py

 from .public_interface import *

my_module.public_interface.__init__.py

 from . import my_sub_module from .some_more_code import *

my_module.public_interface.my_sub_module.__init__.py

 from .code import *

只要用户只导入顶级模块,这就能正常工作:

import my_module

my_module.my_sub_module.whatever  # Works as intended

但是,这不起作用:

from my_module import my_sub_module

也不:

import my_module.my_sub_module

我必须改变什么才能使这最后两个进口工作?

导入系统只允许将实际的包和模块作为带点的模块名称的一部分直接导入,但是您的:

from .public_interface import *

hack 只是使my_sub_module成为my_module包的一个属性,而不是用于导入系统的实际子模块。 它因为同样的原因而中断:

from collections._sys import *

休息; 是的,作为一个实现细节, collections包碰巧导入别名为_sys sys ,但这实际上并没有使_sys成为collections的子包,它只是collections包上的许多属性之一。 从导入机制的角度来看, my_sub_modulemy_module的子模块,而_syscollections的子模块; 嵌套在my_module下的子目录中这一事实无关紧要。

也就是说,导入系统提供了一个钩子,允许您将其他任意目录视为包的一部分,__path__属性 默认情况下, __path__只包含包本身的路径(因此my_module__path__默认为['/absolute/path/to/my_module'] ),但您可以根据需要以编程方式对其进行操作; 解析子模块时,它只会搜索__path__的最终内容,就像导入顶级模块搜索sys.path 因此,要解决您的特定情况(希望public_interface所有包/模块都可以导入,而无需在导入行中指定public_interface ),只需将my_module/__init__.py文件更改为具有以下内容:

import os.path
__path__.append(os.path.join(os.path.dirname(__file__), 'public_interface'))

所做的只是告诉导入系统,当import mymodule.XXXX发生时( XXXX是真实姓名的占位符),如果它找不到my_module/XXXXmy_module/XXXX.py ,它应该寻找my_module/public_interface/XXXXmy_module/public_interface/XXXX.py 如果你想让它先搜索public_interface ,把它改成:

__path__.insert(0, os.path.join(os.path.dirname(__file__), 'public_interface'))

或者让它检查public_interface (所以直接在my_module下没有任何东西是可导入的),使用:

__path__[:] = [os.path.join(os.path.dirname(__file__), 'public_interface')]

完全替换__path__的内容。


旁注:您可能想知道为什么os.path是此规则的一个例外; 在 CPython 上, os是一个带有属性path的普通模块(它恰好是模块posixpathntpath取决于平台),但您可以执行import os.path 这是有效的,因为os模块在被导入时,显式(和hackily)填充了os.pathsys.modules缓存。 这是不正常的,它有性能成本; import os必须始终隐式地导入os.path ,即使从未使用过os.path中的任何内容。 __path__避免了这个问题; 除非要求,否则不会导入任何内容。

可以通过使my_module/__init__.py包含以下内容获得相同的结果:

import sys
from .public_interface import my_sub_module

sys.modules['my_module.my_sub_module'] = my_sub_module

这将允许人们在只完成import my_module使用my_module.my_submodule ,但这将强制my_module任何import导入public_interfacemy_sub_module ,即使从未使用过my_sub_module中的任何内容。 由于历史原因, os.path继续这样做(很久以前只使用os.path API 和import os ,并且很多代码依赖于这种错误行为,因为程序员很懒惰并且它有效),但是新代码不应该使用这个黑客。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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