如何在导入的Python库内部设置调用脚本的__package__属性
你可以通过inspect模块获取调用方对应的模块对象,直接修改其__package__属性即可,完整的relative_import库实现代码如下:
import inspect import pathlib import imptools import sys def enable(): # 跳过当前库的栈帧,获取调用enable()的脚本栈帧 for frame_info in inspect.stack(): if frame_info.filename == __file__: continue caller_frame = frame_info.frame break # 获取调用脚本对应的模块对象 caller_module = inspect.getmodule(caller_frame) # 仅处理直接作为主脚本运行、且__package__未设置的场景 if caller_module.__name__ == "__main__" and caller_module.__package__ is None: script_path = pathlib.Path(caller_frame.f_globals["__file__"]) # 计算包名,多层包结构可自行调整为递归查找__init__.py生成完整包名 package_name = script_path.parent.name # 直接修改调用方模块的__package__属性,效果等价于在脚本中直接赋值 caller_module.__package__ = package_name # 执行原有包导入逻辑 imptools.import_from_path(package_name, "..") # 可选:将顶层包路径加入sys.path,确保相对导入可以找到上层模块 sys.path.insert(0, str(script_path.parent.parent.resolve()))
原理解释
Python中所有运行的模块都是模块类的实例对象,脚本作为主程序运行时对应的是__main__模块。你通过inspect.getmodule()拿到的调用方模块对象,和脚本自身运行时的上下文是同一个对象,直接修改其__package__属性,和在脚本代码里手动写__package__ = "xxx"的效果完全一致。
注意事项
如果你的脚本处于多层嵌套的包结构下(例如parent/child/grandson/script.py),需要调整包名计算逻辑,递归查找父目录下的__init__.py文件,生成完整的点分格式包名(例如parent.child.grandson),才能支持跨多层的相对导入。
内容的提问来源于stack exchange,提问作者danijar
相关产品推荐
相关产品推荐

