这不是我第一次对Python中的imports
感到尴尬。但我想这是一个有趣的用例,所以我想在这里询问它,以获得更好的见解。我的项目结构如下:
sample_project
- src
- __init__.py
- module1
- __init__.py
- utils.py
- module2
- __init__.py
- models.py
- app.py
module1
从module2
导入方法,app
从其他所有方法导入方法。此外,当您运行app
时,它需要在src
文件夹之外创建一个名为logs
的文件夹。现在有运行应用程序的方法:
- 从
src
文件夹内flask run app
- 从
src
文件夹外flask run src.app
为了确保我不会因为启动应用程序的顶级模块的更改而获得import errors
,我这样做:
import sys
sys.path.append("..")
这个问题有更好的解决办法吗?
导入问题的Python解决方案是不向任何可能用作顶级脚本(包括单元测试(的文件添加sys.path(或间接添加PYTHONPATH(黑客,因为这会使代码库难以更改和维护。假设您必须重新组织项目结构或重命名文件夹。
相反,这就是editable installs
的用途。它们可以通过两种方式实现:
- pip安装-可编辑
<path>
(需要基本的setup.py( - conda开发
<path>
(需要conda-build
包(
无论哪种方式,都会将符号链接添加到您的站点软件包文件夹中,并使您的本地项目看起来像是完全安装好的,同时您可以继续编辑。
永远记住:保持事物易于更改
经过研究(here1,here2,here3,here4,here5,here6(,我在这个时候想出了更好的解决方案。这是在每个python文件中,您可以在导入之前添加其当前路径。下面的示例代码:
import os
import sys
if os.path.dirname(os.path.abspath(__file__)) not in sys.path:
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
查看Python导入系统文档和PYTHONPATH环境变量。
当您的代码执行import X.Y
时,Python运行库所做的是在PYTHONPATH
中列出的每个文件夹中查找包含Y
包的包X
(包只是包含__init__.py
文件的文件夹(。
大多数情况下,附加到sys.path
是一个糟糕的解决方案。最好注意PYTHONPATH的设置:检查它是否包含root目录(包含顶级包(,而不包含其他内容(除了site-packages
,i(。然后,无论您在哪里运行命令,它都将正常工作(至少对于导入,os.cwd
是另一个问题(。
根据运行Python脚本的方式,.
可能是其中唯一相关的路径,因此它取决于您的当前目录,如果您在顶级包中运行它,则需要附加..
。
也许您不应该从不是项目根目录的目录中运行脚本?
TL;DR:一个好的PYTHONPATH
可以减少导入错误。
将flask
命令封装到sample_project
目录中的一个小脚本中,并根据您的项目设置PYTHONPATH
:
#!/bin/env bash
# Assuming script is sample_project
path=`dirname ${BASH_SOURCE[0]}`
full_path=`realpath "$p"`
export PYTHONPATH=$full_path/src:$PYTHONPATH
flask run app
您还可以将当前目录切换到工作目录。
但最好使用setuptools
打包您的项目并安装它(可能在开发模式下(,根据PYTHONUSERBASE
在用户空间中或在虚拟环境中安装。