问题描述
有如下项目目录:
website-monitor/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ ├── utils.py
│ └── templates/
│ └── index.html
├── scripts/
│ └── init_db.py
├── websites.json
├── requirements.txt
└── Dockerfile
运行项目前,需要提前使用scripts/init_db.py程序来初始化数据库。在初始化代码中引用了上级目录中的模块,如下:
from app.models import Database
def init_db():
db = Database(host="db", user="vscode", password="vscode", dbname="website_monitor")
db.execute("""
CREATE TABLE IF NOT EXISTS performance_data (
id SERIAL PRIMARY KEY,
site TEXT NOT NULL,
page_load_time INTEGER,
ttfb INTEGER,
resource_load_time INTEGER,
dom_render_time INTEGER,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
db.close()
if __name__ == "__main__":
init_db()
如果没有特别配置的话,有可能会提示找不到模块的错误。
$→:python3 init_db.py
Traceback (most recent call last):
File "/workspace/scripts/init_db.py", line 1, in <module>
from app.models import Database
ModuleNotFoundError: No module named 'app'
这个错误是由于python无法找到app目录,无法正确引用app模块。
解决方案
为了解决这个问题,需要修改导入语句,将项目根目录添加到sys.path。在init_db.py 中添加以下代码即可以解决引入问题:
import sys
import os
# 将项目根目录添加到 Python 路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
前提条件是在app目录下应当有__init__.py文件,表明这是个包文件。
详细解释
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
这行代码的作用是将项目根目录添加到 Python 的模块搜索路径中,以便 Python 能够正确找到并导入项目中的模块(如 app
模块)。
1. __file__
__file__
是一个特殊变量,表示当前脚本的文件路径。例如,如果
init_db.py
的路径是/workspace/app/scripts/init_db.py
,那么__file__
的值就是"/workspace/app/scripts/init_db.py"
。
2. os.path.abspath(__file__)
os.path.abspath()
函数用于获取文件的绝对路径。例如,如果
__file__
是"init_db.py"
,那么os.path.abspath(__file__)
会返回"/workspace/app/scripts/init_db.py"
。
3. os.path.dirname(path)
os.path.dirname()
函数用于获取路径的父目录。例如:
os.path.dirname("/workspace/app/scripts/init_db.py")
返回"/workspace/app/scripts"
。os.path.dirname("/workspace/app/scripts")
返回"/workspace/app"
。
4. os.path.dirname(os.path.dirname(path))
这里嵌套调用了两次
os.path.dirname()
,目的是获取当前脚本的祖父目录(即项目根目录)。例如:
os.path.dirname("/workspace/app/scripts/init_db.py")
返回"/workspace/app/scripts"
。os.path.dirname("/workspace/app/scripts")
返回"/workspace/app"
。
5. sys.path.append(path)
sys.path
是 Python 的模块搜索路径列表。Python 在导入模块时,会按照sys.path
中的路径顺序查找模块。sys.path.append(path)
将指定的路径添加到sys.path
中,使得 Python 能够在该路径下查找模块。
最终,
"/workspace/app"
被添加到sys.path
中。
原理
在 Python 中,模块的导入是基于 sys.path
中的路径进行查找的。默认情况下,sys.path
包含以下路径:
当前脚本所在的目录。
Python 标准库路径。
环境变量
PYTHONPATH
中指定的路径。
如果 init_db.py
需要导入 app
模块,而 app
模块位于项目根目录(/workspace/app
),则需要将项目根目录添加到 sys.path
中,否则 Python 会报错 ModuleNotFoundError: No module named 'app'
。