[英]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_module
是my_module
的子模块,而_sys
是collections
的子模块; 嵌套在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/XXXX
或my_module/XXXX.py
,它应该寻找my_module/public_interface/XXXX
或my_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
的普通模块(它恰好是模块posixpath
或ntpath
取决于平台),但您可以执行import os.path
。 这是有效的,因为os
模块在被导入时,显式(和hackily)填充了os.path
的sys.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_interface
和my_sub_module
,即使从未使用过my_sub_module
中的任何内容。 由于历史原因, os.path
继续这样做(很久以前只使用os.path
API 和import os
,并且很多代码依赖于这种错误行为,因为程序员很懒惰并且它有效),但是新代码不应该使用这个黑客。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.