首页 > 解决方案 > 如何从 Python 的父目录中的包中导入模块?

问题描述

我很难理解 Python 在哪里搜索包和模块。考虑以下目录结构:

.
├── pkg
│   ├── __init__.py
│   └── module.py
└── scripts
    ├── __init__.py
    └── join.py

所有__init__.py文件都是空的,pkg/module.py是:

############################################################
# File Name : module.py
############################################################

def method(*args):
    return ', '.join(*args)

并且scripts/join.py是:

############################################################
# File Name : join.py
############################################################

import os
import sys

from pkg import module


def join(*args):
    print(module.method(args))

if __name__ == "__main__":
    join(*sys.argv[1:])
    sys.exit(os.EX_OK)

现在,如果我从根目录运行交互式 python 会话。我可以毫无问题地导入scripts.join并运行该方法:join

>>> from scripts.join import join
>>> join('a', 'B', 'c', 'D')
a, B, c, D

但是,如果我想join直接从命令行运行,我会收到导入错误:

$ python3 scripts/join.py 
Traceback (most recent call last):
  File "scripts/join.py", line 11, in <module>
        from pkg import module
ModuleNotFoundError: No module named 'pkg'

令人惊讶的是,如果我移动join.py到根目录,它可以作为脚本调用:

$ cp scripts/join.py .
$ python3 join.py a b C d E
a, b, C, d, E

所以我想知道在交互式会话中导入和在脚本中导入有什么区别。另外,我如何能够保留目录join.py内部scripts/(同时能够从命令行调用它而无需交互式 Python 会话)?


更新:我意识到如果我调整sys.path问题就会scripts/join.py消失:

############################################################
# File Name : join.py
############################################################

import os
import sys

sys.path.append('')

from pkg import module


def join(*args):
    print(module.method(args))

if __name__ == "__main__":
    join(*sys.argv[1:])
    sys.exit(os.EX_OK)

现在我可以运行:

$ python3 scripts/join.py a b c d 
a, b, c, d

答案中也提出了类似的解决方案。

我想知道这样的调整是安全的还是有更 Pythonic 的解决方案?例如,很明显,如果脚本是从任何其他目录调用的,它将无法工作。有没有更明确的方式要求 Pythonpkg从父目录导入?(一种可能性是将相对于__file__脚本中路径的路径附加到sys.path

我尝试__init__.py在根目录中添加一个并导入

from ..pkg import module

但在那种情况下,我总是得到

ValueError: attempted relative import beyond top-level package

标签: pythonpython-3.x

解决方案


因为python会尝试从当前文件夹导入文件夹(或py文件)(如果找不到,它会尝试导入你已经安装的模块)。这就是为什么如果你放到join.py根文件夹,它可以正常运行。

.
├── pkg
│   ├── __init__.py
│   └── module.py
└── scripts
    ├── __init__.py
    └── join.py

如果你想from pkg import module使用join.py

在你的join.py

import sys
sys.path.append("..") # python will try to find the module in his parent folder
from pkg import module

python 文档中:

sys.path:指定模块搜索路径的字符串列表。从环境变量 PYTHONPATH 初始化,加上依赖于安装的默认值。

一开始只有两个路径可以导入sys.argv[0]__file__.(包括系统PATH中的python文件夹)。使用sys.path.append("..")后可以导入三个路径。

我认为只有你有许多具有相同名称的文件会导致一些问题。


推荐阅读