Python模块导入异常:from common.library-common导入失败求无重构方案
问题描述
项目目录结构
Pipfile module-common\ __init__.py setup.py common\ library-common.py module-a\ __init__.py setup.py functional-module\ library-a.py library-b.py
Pipfile配置
... [packages] module-common = {file = "module-common", editable = true} module-a = {file = "module-a", editable = true}
setup.py配置
module-common/setup.py:
from setuptools import find_packages, setup setup( name="module-common", version="2", ... packages=find_packages(), ... )
module-a/setup.py:
from setuptools import find_packages, setup setup( name="module-a", version="2", ... packages=find_packages(), ... )
所有__init__.py文件均为空,执行以下命令完成开发模式安装:
pipenv install -e module-common pipenv install -e module-a pipenv install
执行pip list确认所有包已正确安装。
导入错误情况
在library-a.py中尝试使用如下导入语句时触发报错:
from common.library-common import SomeClass print(f"function return: {SomeClass.function()}")
报错信息:
NameError: name 'library-common' is not defined
但改用如下导入语句则可正常运行:
import common print(f"function return: {common.common.library_common.SomeClass.function()}")
需求:不修改library-a的代码,让第一种导入方式生效。
解决方案
问题本质
- 文件名连字符的语法冲突:Python的模块名属于标识符,不允许包含连字符
-,library-common.py中的-会被解析成减号,导致from common.library-common的语法逻辑错误。 - 模块名的自动转换规则:Python导入时会自动将文件名中的连字符转为下划线,所以实际可访问的模块名是
library_common;通过import common后用属性访问的方式,Python会自动适配这个转换,因此common.common.library_common能正常工作。
无需修改library-a代码的解决方法
方法1:通过__getattr__实现别名映射
修改module-common/common/__init__.py,添加以下代码:
def __getattr__(name): if name == 'library-common': import importlib return importlib.import_module('common.library_common') raise AttributeError(f"module 'common' has no attribute '{name}'")
当Python尝试导入common.library-common时,会调用这个__getattr__方法,自动映射到实际的library_common模块,从而让原导入语句生效。
方法2:直接注册模块别名
同样修改module-common/common/__init__.py,通过sys.modules直接注册别名:
import sys from . import library_common sys.modules['common.library-common'] = library_common
这会直接将common.library-common这个模块名绑定到实际的library_common模块,Python在执行原导入语句时会直接找到对应的模块。
补充说明
如果不想修改module-common的代码,也可以在项目启动前的初始化脚本中执行上述别名注册逻辑,但这种方式需要额外维护初始化代码,不如直接修改module-common的__init__.py来得简洁可靠,因为这属于module-common自身的模块适配工作。
内容的提问来源于stack exchange,提问作者Ozeta
相关产品推荐
相关产品推荐

