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

Python模块间共享资源的最佳实践

Python模块间共享资源的最佳实践

这种场景我太熟悉了——写可复用模块的时候,既想让入口脚本(比如你的main.py)统一管理资源,又不想让子模块和入口脚本绑定死,免得以后别的脚本导入子模块还要改一堆代码。你现在用set_logger的方法确实有点繁琐,我给你分享几个更Pythonic的方案,从简单到进阶都有:

一、先解决日志的特殊情况:利用logging的层级特性

你举的日志例子其实不用手动传递logger实例——Python的logging模块本身就是按命名空间层级继承配置的。

比如在main.py里:

import logging
# 配置根日志(或者当前模块的logger)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.info('Hello world')

然后在my_module_1/a.py里,只需要这样写:

import logging
# 用当前模块的命名空间获取logger,它会自动继承父级(包括根日志)的配置
logger = logging.getLogger(__name__)
logger.info('Logging from a.py')

这样a.py里的logger会自动应用main.py里的配置,完全不用手动传递实例,这是logging模块设计好的特性,专门解决这种跨模块日志配置的问题。

二、通用资源的共享方案(数据库、缓存、配置等)

对于日志之外的资源,比如数据库连接、缓存实例、全局配置,我们需要更通用的方法,下面几种是常用的:

1. 轻量级全局资源容器(简单易实现)

不用第三方库,自己写一个简单的容器类来统一管理资源,避免每个模块都写set_xxx方法。

比如新建一个common.py:

class ResourceContainer:
    def __init__(self):
        self.logger = None
        self.db_conn = None
        self.cache = None

# 创建全局单例容器
container = ResourceContainer()

然后在main.py里初始化资源并放入容器:

from common import container
import logging
import psycopg2

# 初始化日志
container.logger = logging.getLogger(__name__)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)

# 初始化数据库连接
container.db_conn = psycopg2.connect("dbname=test user=postgres")

之后任何子模块(比如my_module_1/a.py)直接从容器里取资源即可:

from common import container

def do_db_operation():
    container.logger.info("Executing DB operation")
    cursor = container.db_conn.cursor()
    # 执行数据库操作...

这种方法比单个模块写set方法简洁很多,而且所有资源统一管理,也方便后续扩展。注意如果是多线程/多进程场景,要给容器的资源访问加锁,避免并发问题。

2. 工厂函数/构造器注入(最解耦,推荐)

如果想让子模块完全独立,不依赖任何全局状态,最好的方式是让子模块的函数或类接受资源作为参数,也就是依赖注入的基础形式。

比如my_module_1/a.py里:

class DataProcessor:
    def __init__(self, logger, db_conn):
        # 直接通过构造器注入依赖
        self.logger = logger
        self.db_conn = db_conn

    def process_data(self):
        self.logger.info("Starting data processing")
        # 使用db_conn处理数据...

def fetch_data(logger, db_conn):
    logger.info("Fetching data from DB")
    cursor = db_conn.cursor()
    # 查询数据...

然后在main.py里:

from my_module_1.a import DataProcessor, fetch_data
import logging
import psycopg2

# 初始化资源
logger = logging.getLogger(__name__)
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
db_conn = psycopg2.connect("dbname=test user=postgres")

# 注入资源到子模块的类/函数
processor = DataProcessor(logger, db_conn)
processor.process_data()

fetch_data(logger, db_conn)

这种方式的好处是子模块完全不知道main.py的存在,只关心自己需要的资源类型,测试的时候还可以传入mock的logger或db_conn,非常灵活。如果觉得每次传参数麻烦,可以在main里写一个工厂函数来封装资源注入。

3. 第三方依赖注入库(适合大型项目)

你提到的dependency_injector确实是个不错的选择,但它更适合资源多、依赖关系复杂的大型项目。如果你的项目比较小,或者不想给子模块的使用者强加外部依赖,那前面两种轻量级方案就足够了。

如果真的要用dependency_injector,它可以帮你管理资源的生命周期(比如单例、请求级实例),自动注入依赖,代码会更规整,但学习成本和复杂度会高一些,需要权衡。

三、总结最佳实践

  • 对于日志这类有内置层级机制的资源,优先利用模块自身的特性,不用手动传递。
  • 通用资源优先选择构造器/函数参数注入,解耦性最强,可测试性最好。
  • 如果需要全局共享资源实例,且不想每次传递参数,用轻量级全局容器是简洁的选择,注意线程安全。
  • 大型项目或复杂依赖场景再考虑使用成熟的依赖注入库,避免过度设计。

备注:内容来源于stack exchange,提问作者user2526586

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:53:03