Python包引用函数报错(TypeError: 'module' object is not callable)
解决单函数单文件包结构下的导入错误与循环导入问题
问题背景
执行pytest单元测试时触发错误:
E TypeError: 'module' object is not callable
项目目录结构如下:
├───src │ └───company │ ├───acc │ └───dp │ └───logic │ ├───business │ │ ├───__init__.py │ │ ├───alter_customer.py │ │ ├───filter_customer.py │ │ └───review_customers.py │ └───general │ ├───some_function.py │ └───__init__.py ├───tests │ └───company │ └───dp │ └───logic │ └───business │ ├───test_alter_customer.py │ └───test_review_customers.py ├───conftest.py └───pyproject.toml
business包下每个文件包含一个与文件名同名的函数,例如filter_customer.py内容:
def filter_customer(i: int) -> int: return i
错误触发场景:
- 在
alter_customer.py中用from company.dp.logic.business import filter_customer导入并调用时出错 - 直接从文件导入(
from src.company.dp.logic.business.filter_customer import filter_customer)或跨包导入(如从general包导入函数)可正常运行 - 修改函数名与文件名不同时出现循环导入错误
- 将导入语句放在函数内部则可正常执行
原因分析
- TypeError根源:当使用
from company.dp.logic.business import filter_customer时,导入的是同名的filter_customer模块对象,而非模块内的同名函数——因为business/__init__.py未显式将函数导出为包的成员。 - 循环导入问题:修改函数名与文件名不同后,若在
__init__.py或模块顶层互相导入,容易触发Python的循环导入检测,导致报错。
规范解决方案(实现单函数单文件+包统一导入)
1. 完善business/__init__.py显式导出函数
修改src/company/dp/logic/business/__init__.py,将每个模块内的同名函数导入并暴露为包的公共接口:
from .filter_customer import filter_customer from .alter_customer import alter_customer from .review_customers import review_customers
此时在alter_customer.py中,可使用绝对导入:
from company.dp.logic.business import filter_customer
或更简洁的相对导入:
from . import filter_customer
两种方式导入的都是函数对象,不会再触发TypeError。
2. 用相对导入降低循环导入风险
包内模块间的导入优先使用相对路径(如from .filter_customer import filter_customer),相比绝对导入更清晰,也能减少跨模块导入时的循环依赖概率。配合__init__.py的显式导出,外部代码(如测试模块)仍可通过包名统一导入:
from company.dp.logic.business import alter_customer
3. 延迟导入(临时应急,不推荐长期使用)
如果暂时无法调整__init__.py,可将导入语句放在函数内部延迟加载,避免顶层导入引发的循环问题,但这种方式会分散导入逻辑,不利于代码维护:
def alter_customer(param): from .filter_customer import filter_customer # 调用filter_customer处理逻辑
验证与注意事项
- 确保
src目录被设为Python源码根:可通过pyproject.toml配置(如使用Poetry或Setuptools),避免导入时需要带src前缀。 - 配置pytest识别源码根:在
pyproject.toml中添加如下配置,确保测试模块能正确导入业务代码:[tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] - 保持包接口清晰:通过
__init__.py显式导出函数,能让包的对外接口一目了然,避免使用者混淆模块和函数。
内容的提问来源于stack exchange,提问作者Paweł Tajs
相关产品推荐
相关产品推荐

