当前位置:首页>python>别再手写单例模式了,Python 的这个模块天然支持,一行搞定.

别再手写单例模式了,Python 的这个模块天然支持,一行搞定.

  • 2026-09-10 03:24:50
别再手写单例模式了,Python 的这个模块天然支持,一行搞定.

你肯定见过这样的代码,一个类被翻来覆去写了七八遍,就为了确保它只有一个实例。每次写单例模式,先定义类变量,再改__new__方法,或者搞个装饰器。我见过最夸张的同事,一个单例写了四十行代码,老老实实加锁、判断、双重检查锁。说实话,大部分场景根本不需要这么复杂。

Python 里有个模块叫 importlib,它天然支持单例。你不需要写任何特殊代码,因为 Python 的模块在第一次导入后就会被缓存到 sys.modules 里。同一个模块不管被导入多少次,永远只执行一次初始化代码。这就是最干净的单例模式。

举个例子,你创建一个文件叫 config.py,里面写:

 class AppConfig:
   def __init__(self):
     self.name = '我的应用'

config = AppConfig()

然后在其他地方导入这个模块:
from config import config

不管你调多少次 import,config 永远是同一个对象。模块加载的瞬间对象就创建好了,你不需要操心多线程问题,不需要写锁。Python 解释器在导入模块时已经帮你处理好了线程安全。

有人会问,如果我想延迟初始化怎么办。很简单,用函数封装一下:

 database.py
 _db_instance = None

 def get_db():
   global _db_instance
   if _db_instance is None:
     _db_instance = DBCnnection()

  return _db_instance

然后在别的文件里 from database import get_db。这比手写类单例清爽多了。

我见过不少新手用传统的方式写单例,把一个简单的配置类变得又长又难看。代码量翻了三倍,可读性降了一半。用模块天然的单例特性,代码短,逻辑直白,别人一看就懂。你写完这个,同事接你代码都不用皱眉。

再说个实际案例。你写一个日志记录器,通常全局只需要一个。以前我写过这样的:

 import logging

 def get_logger():
   return logging.getLogger('my_app')

logger = get_logger()

把这个放在 logger.py 里,到处导入 logger 就行。logging 模块内部本来就有缓存机制,加上模块的单例,双重保险。你再也不用担心日志重复输出的问题。

有一种观点说,用模块做单例不够灵活,没办法方便地重置状态。这话没错,但大部分业务场景根本不需要重置。如果你非要重置,直接清一下 sys.modules 就行。不过别乱用,这是个脏操作,生产环境不推荐。

说真的,我看了不少开源项目,很多高质量的 Python 库都直接用模块变量做单例。比如 requests 库内部,session 对象就是模块级别创建的。人家几千星的项目都这么干,你何必自己手写一堆申明。

最后提醒一句,如果你用 __init__.py 里的变量做单例,记得小心循环导入。如果两个模块互相导入,Python 会报错。这个坑顶多浪费你十分钟,写法上注意单向依赖就行。

最新文章

随机文章