You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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包导入函数)可正常运行
  • 修改函数名与文件名不同时出现循环导入错误
  • 将导入语句放在函数内部则可正常执行

原因分析

  1. TypeError根源:当使用from company.dp.logic.business import filter_customer时,导入的是同名的filter_customer模块对象,而非模块内的同名函数——因为business/__init__.py未显式将函数导出为包的成员。
  2. 循环导入问题:修改函数名与文件名不同后,若在__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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:22:02