python 2.5中的相对导入

Posted

技术标签:

【中文标题】python 2.5中的相对导入【英文标题】:Relative imports in python 2.5 【发布时间】:2012-01-08 02:44:45 【问题描述】:

我知道有很多关于 Python 中相同导入问题的问题,但似乎没有人设法提供正确用法的明确示例。

假设我们有一个包mypackage,其中包含两个模块foobar。在foo 内部,我们需要能够访问bar

因为我们还在开发中,mypackage 不在sys.path 中。

我们希望能够:

导入mypackage.foofoo.py 作为脚本运行并从__main__ 部分执行示例用法或测试。 使用 Python 2.5

我们必须如何在 foo.py 中进行导入,以确保它在所有这些情况下都能正常工作。

# mypackage/__init__.py
...

# mypackage/foo/__init__.py
...

# mypackage/bar.py  
def doBar()
    print("doBar")

# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package

def doFoo():
    print(doBar())

if __name__ == '__main__':
    doFoo()

【问题讨论】:

我相信你必须区分可导入模块和脚本。我不确定我是否同意这种设计,但这是我对它的理解。 通常,当您需要从上层导入包时,您做错了:您是否希望将bar 用作库而不是 mypackage 的子模块? IE。您能否将 mypackage 重构为 mypackage1 和 mypackage2 ,其中 mypackage2(带有 foo)导入 mypackage1(带有 bar)? 【参考方案1】:

看看来自PEP 328的以下信息:

相对导入使用模块的__name__ 属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为'__main__'),那么无论模块实际位于文件系统中的什么位置,相对导入都会被解析为模块是***模块。

当您将foo.py 作为脚本运行时,该模块的__name__'__main__',因此您无法进行相对导入。即使mypackagesys.path 上也是如此。基本上,您只能从一个模块中进行相对导入,前提是该模块已被导入。

以下是解决此问题的几个选项:

1) 在foo.py 中,检查是否__name__ == '__main__' 并有条件地将mypackage 添加到sys.path

if __name__ == '__main__':
    import os, sys
    # get an absolute path to the directory that contains mypackage
    foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
    sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
    from mypackage import bar
else:
    from .. import bar

2) 始终使用from mypackage import bar 导入bar,并以mypackage 自动可见的方式执行foo.py

$ cd <path containing mypackage>
$ python -m mypackage.foo.foo

【讨论】:

谢谢!唯一还不清楚的是包路径的检测。我明天会检查并返回结果。 @sorin - 我刚刚编辑了我的答案,因此无论您从哪里调用 foo.py,您都可以获得 mypackage 的路径。 在 Windows 上,我发现getcwd() 没有提供脚本目录的情况很少。调用 python 脚本的方法至少有 3 种:“python a.py”、“a.py”,以及从 Windows 资源管理器运行(最终通过 pythonw) @sorin - 不幸的是我只能在 Linux 上进行测试,我看到的行为是 __file__ 要么是绝对路径,要么是从 os.getcwd()foo.py 的路径。在这两种情况下,foo_dir 是包含foo.py 的目录的绝对路径。如果 Windows 不是这种情况,则不确定需要如何更改。 @sorin - 如果你需要使用来自 bar 的doBar,你要么需要使用from mypackage.bar import doBar 将其导入foo 的命名空间,要么使用from mypackage import bar 并将其命名为bar.doBar()。跨度> 【参考方案2】:

我的解决方案看起来更简洁,可以与所有其他导入一起位于顶部:

try:
   from foo import FooClass
except ModuleNotFoundError:
   from .foo import FooClass

【讨论】:

感谢您的回答!它确实帮助了我很多!

以上是关于python 2.5中的相对导入的主要内容,如果未能解决你的问题,请参考以下文章

Python中的“尝试相对导入超出***包”错误意味着啥?

详解Python中的相对导入和绝对导入

python中的绝对导入不起作用,相对导入起作用

Jupyter Notebook 中的 Python 相对导入

Python 中的相对导入

从 Python 中的相对路径导入