首页 > 解决方案 > 存在同名同级目录时导入 Python 模块

问题描述

我有一个项目结构如下:

python-test » tree
.
├── asdf
│   ├── __init__.py
│   ├── mycode2.py
│   ├── mycode.py
│   └── scripts
│       └── __init__.py
├── __init__.py
└── scripts
    ├── __init__.py
    └── mymod.py

asdf/mycode.py包含:

import scripts.mymod

scripts/mymod.py包含:

print('hello world')

所有的__init__.pys 都是空的。

我想导入scripts.mymod.

我正在运行asdf/mycode.py失败,因为它正在查看内部asdf/scriptsscripts不是mymod.

> PYTHONPATH=: python asdf/mycode.py
Traceback (most recent call last):
  File "asdf/mycode.py", line 1, in <module>
    import scripts.mymod
ModuleNotFoundError: No module named 'scripts.mymod'

作为一种解决方法,我可以手动修改路径,我在asdf/mycode2.py.

import sys
sys.path = [p for p in sys.path if 'asdf' not in p]
import scripts.mymod

这可以正常工作:

> PYTHONPATH=: python asdf/mycode2.py
hello world

scripts.mymod有没有一种方法可以在asdf/mycode.py 手动修改sys.path且不更改PYTHONPATH必须设置为的情况下导入:

标签: pythonpathpython-import

解决方案


TL;博士

Python 导入既奇怪又麻烦,您最好在目录顶部创建一个入口点或使用工具来处理路径操作。

Python 导入

Python 有两种不同的处理导入的方法,第一种是标准的: import numpy as np,它是针对环境 PYTHONPATH 解析的,直到找到请求的模块。该路径中包含当前正在执行的文件的目录,您在示例中看到,您需要从sys.path.

python 处理导入的另一种方式是通过相对路径,它总是以.likefrom . import aimport .aor 开头from .. import b。不幸的是,相对导入在它们所在的文件没有直接运行时才有效(如果您在一个模块中有两个文件,其中一个从另一个导入对象,但它们都打算由 和外部脚本)。这是因为 python 使用内置名称global 来解析相对路径,如果文件直接从 shell 运行,则会将其覆盖为"__main__".

考虑一个文件结构:

.
├── a.py
└── b.py

a.py在哪里

import b

并且b.py

print(__name__)

如果你运行:

python3 b.py    # prints "__main__"
python3 a.py    # prints "b"

因此,如果您希望能够导入scripts/mymod.pyfrom asdf/mycode2.py,可以将导入更改为:

from .. import scripts.mymod

但是,如果这样做,您将无法直接运行该asdf/mycode2.py文件,您将需要在其他地方创建第三个文件以导入asdf/mycode2.py并运行该第三个脚本(这些文件称为入口点)。例如:

python-test » tree
.
├── asdf
│   ├── __init__.py
│   ├── mycode2.py
│   ├── mycode.py
│   └── scripts
│       └── __init__.py
├── __init__.py
├── entrypoint.py
└── scripts
    ├── __init__.py
    └── mymod.py

entrypoint.py在哪里

import asdf.mycode2

另一种方法

另一种方法是开发一个工具来处理操作 pythonsys.path以允许相对导入,即使当前文件正在运行"__main__"。完全披露,我目前正在开发一个基于 nodejsrequire功能的工具,称为repyrer,它是 pip-installable

pip3 install repyrer

它允许你做这样的事情:

from repyrer import repyre

mymod = repyre('../scripts/mymod')

即使您直接运行文件,它也可以工作。

希望有帮助!


推荐阅读