简体   繁体   English

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

[英]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_modulemy_module的子模块,而_syscollections的子模块; 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/XXXXmy_module/XXXX.py ,它应该寻找my_module/public_interface/XXXXmy_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的普通模块(它恰好是模块posixpathntpath取决于平台),但您可以执行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.pathsys.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_interfacemy_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.

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