在导入路径中跳过目录名,方法是在

2024-10-01 15:34:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我对__init__.py中的导入动态感到困惑。 假设我有这样的结构:

package
├── __init__.py
└── subpackage
    ├── __init__.py
    └── dostuff.py

我想导入dostuff.py中的内容。我可以这样做:from package.subpackage.dostuff import thefunction,但是我想删除import语句中的subpackage级别,因此它看起来像这样:

^{pr2}$

我试着把这个放进package/__init__.py

from .subpackage import dostuff

我不明白的是:

# doing this works:
from package import dostuff
dostuff.thefunction()

# but this doesn't work:
from package.dostuff import thefunction
# ModuleNotFoundError: No module named 'package.dostuff'

为什么是这样,我如何使from package.dostuff import thefunction工作?在


Tags: frompyimportpackage内容init动态语句
2条回答

我认为实现您想要的唯一方法是实际创建一个package/dostuff.py模块,并将其中需要的所有内容作为from .subpackage.dostuff import thefunction导入。在

关键是,当您在package/__init__.py中使用from .subpackage import dostuff时,不会重命名原始模块。在

更明确地说,下面是一个同时用于导入和package/dostuff.py文件的示例:

# We import the dostuff link from package
>>> from package import dostuff
>>> dostuff
<module 'package.subpackage.dostuff' from '/tmp/test/package/subpackage/dostuff.py'>

# We use our custom package.dostuff
>>> from package.dostuff import thefunction
>>> package.dostuff
<module 'package.dostuff' from '/tmp/test/package/dostuff.py'>
>>> from package import dostuff
>>> dostuff
<module 'package.dostuff' from '/tmp/test/package/dostuff.py'>

# The loaded function is the same
>>> dostuff.thefunction
<function thefunction at 0x7f95403d2730>
>>> package.dostuff.thefunction
<function thefunction at 0x7f95403d2730>

更清楚的说法是:

from X import Y仅当X是实际的模块路径时才有效。 Y相反,可以是本模块中导入的任何项目。在

这也适用于在其__init__.py中声明任何内容的包。这里您在package中声明模块package.subpackage.dostuff,因此您可以导入并使用它。在

但是如果您尝试使用模块进行直接导入,那么它必须存在于文件系统中

资源:

我希望这能让事情更清楚

事实上,通过修改Python的sys.modulesdict,您可以很容易地伪造这一点,问题是您是否真的需要这样做,或者花一点时间考虑一下您的包结构是否好。在

就我个人而言,我会考虑这种糟糕的风格,因为它对模块和包的名称应用了魔力,而可能使用和扩展您的包的人很难弄清楚到底发生了什么。在

按照上面的结构,将以下代码添加到package/__init__.py

import sys

from .subpackage import dostuff

# This will be package.dostuff; just avoiding to hard-code it.
_pkg_name = f"{__name__}.{dostuff.__name__.rsplit('.', 1)[1]}"

if _pkg_name not in sys.modules.keys():
    dostuff.__name__ = _pkg_name  # Will have no effect; see below
    sys.modules[_pkg_name] = dostuff

这会将dostuff模块从subpackage导入到package的作用域,更改其模块路径并将其添加到导入的模块中。本质上,这只是将模块的绑定复制到另一个导入路径,其中成员内存地址保持不变。只需复制引用:

^{pr2}$

。。。收益率

<module 'package.subpackage.dostuff' from '/path/package/subpackage/dostuff.py'>
<module 'package.subpackage.dostuff' from '/path/package/subpackage/dostuff.py'>
<function something_to_do at 0x1029b8ae8>
<function something_to_do at 0x1029b8ae8>

请注意

  1. 模块名package.subpackage.dostuff即使在package/__init__.py中更新,也没有更改
  2. 函数引用相同:0x1029b8ae8

现在,你也可以走了

from package.dostuff import something_to_do
something_to_do()

但是,要谨慎。在导入模块期间更改导入的模块可能会产生意外的副作用(更新sys.modules和从package内导入其他子包或子模块的顺序也可能相关)。通常,你通过应用这种“改进”来购买额外的工作和额外的复杂性。最好还是建立一个适当的包结构并坚持下去。在

相关问题 更多 >

    热门问题