Python 实现单例模式的五种写法
小伙伴们有没有觉得学习文章很有意思?有意思就对了!今天就给大家带来《Python 实现单例模式的五种写法》,以下内容将会涉及到,若是在学习中对其中部分知识点有疑问,或许看了本文就能帮到你!
单例模式(Singleton Pattern) 是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。
事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
在 Python 中,我们可以用多种方法来实现单例模式:
- 使用模块
- 使用装饰器
- 使用类
- 基于 __new__ 方法实现
- 基于 metaclass 方式实现
下面来详细介绍:
使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
如果我们真的想要一个单例类,可以考虑这样做:
class Singleton(object): def foo(self): pass singleton = Singleton()
将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
from mysingleton import singleton
使用装饰器
def Singleton(cls): _instance = {} def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] return _singleton @Singleton class A(object): a = 1 def __init__(self, x=0): self.x = x a1 = A(2) a2 = A(3)
使用类
class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance
一般情况,大家以为这样就完成了单例模式,但是当使用多线程时会存在问题:
class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance import threading def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
程序执行后,打印结果如下:
<__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton>
看起来也没有问题,那是因为执行速度过快,如果在 __init__ 方法中有一些 IO 操作,就会发现问题了。
下面我们通过 time.sleep 模拟,我们在上面 __init__ 方法中加入以下代码:
def __init__(self): import time time.sleep(1)
重新执行程序后,结果如下:
<__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton>
问题出现了!按照以上方式创建的单例,无法支持多线程。
解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全。
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
打印结果如下:
<__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton>
这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了 time.sleep(20) 后,下面实例化对象时,此时已经是单例模式了。
但我们还是加了锁,这样不太好,再进行一些优化,把 intance 方法,改成下面这样就行:
@classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance
这样,一个可以支持多线程的单例模式就完成了。+
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()
如果用 obj = Singleton(),这种方式得到的不是单例。
基于 __new__ 方法实现
通过上面例子,我们可以知道,当我们实现单例时,为了保证线程安全需要在内部加入锁。
我们知道,当我们实例化一个对象时,是先执行了类的 __new__ 方法(我们没写时,默认调用 object.__new__),实例化对象;然后再执行类的 __init__ 方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式。
import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): pass def __new__(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = object.__new__(cls) return Singleton._instance obj1 = Singleton() obj2 = Singleton() print(obj1,obj2) def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
打印结果如下:
<__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at><__main__.singleton object at></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton></__main__.singleton>
采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton() 。
基于 metaclass 方式实现
相关知识:
- 类由 type 创建,创建类时,type 的 __init__ 方法自动执行,类() 执行 type 的 __call__ 方法(类的 __new__ 方法,类的 __init__ 方法)。
- 对象由类创建,创建对象时,类的 __init__ 方法自动执行,对象()执行类的 __call__ 方法。
例子: class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): pass obj = Foo() # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。 obj()# 执行Foo的 __call__ 方法
元类的使用:
class SingletonType(type): def __init__(self,*args,**kwargs): super(SingletonType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类 print('cls',cls) obj = cls.__new__(cls,*args, **kwargs) cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj) return obj class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType def __init__(self,name): self.name = name def __new__(cls, *args, **kwargs): return object.__new__(cls) obj = Foo('xx')
实现单例模式:
import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self,name): self.name = name obj1 = Foo('name') obj2 = Foo('name') print(obj1,obj2)
文中关于Python,单例模式的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Python 实现单例模式的五种写法》文章吧,也可关注golang学习网公众号了解相关技术文章。

- 上一篇
- 如何重载 golang 方法?

- 下一篇
- 小米汽车加速新能源汽车布局 预计2025年推出第二款纯电SUV
-
- 文章 · python教程 | 3分钟前 |
- Python中的%运算符,你真的会用吗?超实用技巧分享
- 226浏览 收藏
-
- 文章 · python教程 | 8分钟前 |
- Python中的星号"*"到底有何神通?手把手教你搞定各种骚操作
- 434浏览 收藏
-
- 文章 · python教程 | 11分钟前 |
- Python进阶|Manager手把手教你优雅管理共享状态
- 303浏览 收藏
-
- 文章 · python教程 | 16分钟前 |
- Python从入门到大神!100道编程习题挑战
- 330浏览 收藏
-
- 文章 · python教程 | 44分钟前 |
- PyCharm怎么切换成英文?详细设置步骤教程
- 457浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python三大强势领域太秀了,不服都不行!
- 381浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- PyCharm安装&配置全指南,手把手教你快速上手!
- 360浏览 收藏
-
- 文章 · python教程 | 1小时前 | Python zip函数
- Pythonzip函数怎么用?这些技巧和案例帮你快速掌握
- 382浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- VSCode搭建Python环境:插件+调试超详细教程
- 366浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python中int是什么?扒一扒Python整数类型的那些事儿
- 197浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 92次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 100次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 103次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 99次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 97次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览