Python外部脚本作为模块导入时无法读取本地目录文件的原因
问题现象
做API开发时遇到Python路径解析的特殊行为:
- 在
secret_stuff目录下存放token_request.py脚本,功能是读取同目录下的secrets.json配置,构造请求返回API access token,脚本内容如下:
import json import requests def generate_token(): url = json.load(open('./secrets.json'))['endpoints']['token'] # 从配置文件读取token请求端点 data = json.load(open('./secrets.json'))['token_request_data'] # 从配置文件读取token请求参数 r = requests.post(url=url, params=data).json() return r['access_token'] if __name__ == '__main__': generate_token()
直接通过命令行在secret_stuff目录下执行该脚本时可以正常运行。
- 在项目根目录的主程序
api_ingestor.py中将该脚本作为模块导入调用,主程序内容如下:
import json import re import requests import secret_stuff.token_request as tr token = tr.generate_token() endpoint = json.load(open('./secret_stuff/secrets.json'))['endpoints']['KeyMessageData']
项目目录结构为:
项目根目录 ├── secret_stuff │ ├── secrets.json │ └── token_request.py └── api_ingestor.py
此时在根目录执行主程序调用tr.generate_token()会抛出FileNotFound错误,报错点为token_request.py中读取./secrets.json的代码行。
- 如果把
token_request.py里的配置文件路径改成./secret_stuff/secrets.json,主程序调用时不再报错,但直接在secret_stuff目录下执行token_request.py又会报找不到文件的错误,因为该目录下不存在secret_stuff子文件夹。
问题原因
Python中代码里写的相对路径,解析基准不是当前脚本/模块自身所在的目录,而是执行Python命令时终端所处的当前工作目录(CWD, Current Working Directory)。
- 当你直接在
secret_stuff目录下执行python token_request.py时,当前工作目录就是secret_stuff,相对路径./secrets.json会被解析为secret_stuff/secrets.json,路径正确所以可以正常运行。 - 当你在项目根目录执行
python api_ingestor.py时,当前工作目录是项目根目录,哪怕是导入的token_request模块里的相对路径,也会以根目录为基准解析,./secrets.json会被解析为根目录下的secrets.json,但这个文件实际在secret_stuff子目录里,自然触发文件不存在的错误。 - 把路径改成
./secret_stuff/secrets.json后,从根目录启动主程序时路径解析正确,但切回secret_stuff目录直接跑脚本时,相对路径会被解析为secret_stuff/secret_stuff/secrets.json,路径不存在所以又报错。
正确解决方案
不要依赖启动时的工作目录解析路径,通过Python内置的__file__变量获取当前脚本自身的绝对路径,再拼接配置文件路径即可,该方式不受程序启动目录影响,所有场景下都能准确定位目标文件。
修改后的token_request.py代码如下:
import os import json import requests # 获取当前token_request.py所在目录的绝对路径 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) # 拼接secrets.json的绝对路径 SECRETS_PATH = os.path.join(SCRIPT_DIR, 'secrets.json') def generate_token(): with open(SECRETS_PATH, 'r', encoding='utf-8') as f: secrets = json.load(f) url = secrets['endpoints']['token'] data = secrets['token_request_data'] r = requests.post(url=url, params=data).json() return r['access_token'] if __name__ == '__main__': generate_token()
修改后两种运行场景都不会再出现文件找不到的错误。
内容的提问来源于stack exchange,提问作者dbakr
相关产品推荐
相关产品推荐

