[英]Making another module importable from my Python module
I have a Python module directory structure like this:我有一个像这样的 Python 模块目录结构:
my_module
|--__init__.py
|--public_interface
| |--__init__.py
| |--my_sub_module
| | |--__init__.py
| | |--code.py
| |--some_more_code.py
|--other directories omitted
Now, the public_interface
directory (among several others) is only there to organize the code into logical sub-units, as a guideline for me and other developers.现在, public_interface
目录(以及其他几个目录)仅用于将代码组织成逻辑子单元,作为我和其他开发人员的指南。 The eventual user of my_module
shall only see it as my_module.my_sub_module
without the public_interface
in-between. my_module
的最终用户只能将其视为my_module.my_sub_module
,中间没有public_interface
。
I wrote these __init__.py
files:我写了这些__init__.py
文件:
my_module.__init__.py
:my_module.__init__.py
:from .public_interface import *
and和
my_module.public_interface.__init__.py
:my_module.public_interface.__init__.py
:from . import my_sub_module from .some_more_code import *
and和
my_module.public_interface.my_sub_module.__init__.py
:my_module.public_interface.my_sub_module.__init__.py
:from .code import *
This works fine as long as the user imports only the top-level module:只要用户只导入顶级模块,这就能正常工作:
import my_module
my_module.my_sub_module.whatever # Works as intended
However, this does not work:但是,这不起作用:
from my_module import my_sub_module
nor:也不:
import my_module.my_sub_module
What would I have to change to make these last two imports work?我必须改变什么才能使这最后两个进口工作?
The import system only allows actual packages and modules to be imported directly as part of the dotted module name, but your:导入系统只允许将实际的包和模块作为带点的模块名称的一部分直接导入,但是您的:
from .public_interface import *
hack just makes my_sub_module
an attribute of the my_module
package, not an actual submodule for the purposes of the import system. hack 只是使my_sub_module
成为my_module
包的一个属性,而不是用于导入系统的实际子模块。 It breaks for the same reason doing:它因为同样的原因而中断:
from collections._sys import *
breaks;休息; yes, as an implementation detail, the collections
package happens to import sys
aliased to _sys
, but that doesn't actually make _sys
a subpackage of collections
, it's just one of many attributes on the collections
package.是的,作为一个实现细节, collections
包碰巧导入别名为_sys
sys
,但这实际上并没有使_sys
成为collections
的子包,它只是collections
包上的许多属性之一。 From the import machinery's point of view, my_sub_module
is no more a submodule of my_module
than _sys
is of collections
;从导入机制的角度来看, my_sub_module
是my_module
的子模块,而_sys
是collections
的子模块; the fact that nested in a sub-directory under my_module
is irrelevant.嵌套在my_module
下的子目录中这一事实无关紧要。
That said, the import system provides a hook to allow you to treat additional arbitrary directories as being part of package, the __path__
attribute .也就是说,导入系统提供了一个钩子,允许您将其他任意目录视为包的一部分,即__path__
属性。 By default, __path__
just includes the path to the package itself (so my_module
's __path__
defaults to ['/absolute/path/to/my_module']
), but you can programmatically manipulate it however you want;默认情况下, __path__
只包含包本身的路径(因此my_module
的__path__
默认为['/absolute/path/to/my_module']
),但您可以根据需要以编程方式对其进行操作; when resolving submodules, it will search only through the final contents of __path__
, much like importing top level modules searches sys.path
.解析子模块时,它只会搜索__path__
的最终内容,就像导入顶级模块搜索sys.path
。 So to resolve your particular case (wanting all packages/modules in public_interface
to be importable without specifying public_interface
in the import line), just change your my_module/__init__.py
file to have the following contents:因此,要解决您的特定情况(希望public_interface
所有包/模块都可以导入,而无需在导入行中指定public_interface
),只需将my_module/__init__.py
文件更改为具有以下内容:
import os.path
__path__.append(os.path.join(os.path.dirname(__file__), 'public_interface'))
All that does is tell the import system that, when import mymodule.XXXX
occurs ( XXXX
is a placeholder for a real name), if it can't find my_module/XXXX
or my_module/XXXX.py
, it should look for my_module/public_interface/XXXX
or my_module/public_interface/XXXX.py
.所做的只是告诉导入系统,当import mymodule.XXXX
发生时( XXXX
是真实姓名的占位符),如果它找不到my_module/XXXX
或my_module/XXXX.py
,它应该寻找my_module/public_interface/XXXX
或my_module/public_interface/XXXX.py
。 If you want it to search public_interface
first, change it to:如果你想让它先搜索public_interface
,把它改成:
__path__.insert(0, os.path.join(os.path.dirname(__file__), 'public_interface'))
or to have it only check public_interface
(so nothing directly under my_module
is importable at all), use:或者让它只检查public_interface
(所以直接在my_module
下没有任何东西是可导入的),使用:
__path__[:] = [os.path.join(os.path.dirname(__file__), 'public_interface')]
to replace the contents of __path__
entirely.完全替换__path__
的内容。
Side-note: You might wonder why os.path
is an exception to this rule;旁注:您可能想知道为什么os.path
是此规则的一个例外; on CPython, os
is a plain module with an attribute path
(which happens to be the module posixpath
or ntpath
depending on platform), yet you can do import os.path
.在 CPython 上, os
是一个带有属性path
的普通模块(它恰好是模块posixpath
或ntpath
取决于平台),但您可以执行import os.path
。 This works because the os
module, while being imported, explicitly (and hackily) populates the sys.modules
cache for os.path
.这是有效的,因为os
模块在被导入时,显式(和hackily)填充了os.path
的sys.modules
缓存。 This isn't normal, and it has a performance cost;这是不正常的,它有性能成本; import os
must always import os.path
implicitly, even if nothing from os.path
is ever used. import os
必须始终隐式地导入os.path
,即使从未使用过os.path
中的任何内容。 __path__
avoids that problem; __path__
避免了这个问题; nothing is imported unless requested.除非要求,否则不会导入任何内容。
You could achieve the same result by making my_module/__init__.py
contain:您可以通过使my_module/__init__.py
包含以下内容来获得相同的结果:
import sys
from .public_interface import my_sub_module
sys.modules['my_module.my_sub_module'] = my_sub_module
which would allow people to use my_module.my_submodule
having only done import my_module
, but that would force any import
of my_module
to import public_interface
and my_sub_module
, even if nothing from my_sub_module
is ever used.这将允许人们在只完成import my_module
使用my_module.my_submodule
,但这将强制my_module
任何import
导入public_interface
和my_sub_module
,即使从未使用过my_sub_module
中的任何内容。 os.path
continues to do it for historical reasons (using os.path
APIs with only import os
a long time ago, and a lot of code relies on that misbehavior because programmers are lazy and it worked), but new code shouldn't use this hack.由于历史原因, os.path
继续这样做(很久以前只使用os.path
API 和import os
,并且很多代码依赖于这种错误行为,因为程序员很懒惰并且它有效),但是新代码不应该使用这个黑客。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.