单例模式的目的是一个类有且只有一个实例对象存在,比如在复用类的过程中,可能重复创建多个实例,导致严重浪费内存,此时就适合使用单例模式。

前段时间需要用到单例模式,就称着机会在网上找了找,有包含了__new__方法在内的5种单例模式,就顺便记录于此。


基于模块导入机制的实现

第一次执行程序时编译为.pyc文件,而第二次执行时会直接执行.pyc。基于此机制,可以通过把类和所创建的实例单独写在某模块内,在使用时直接从这么模块中导入即可,这个导入的实例对象即唯一对象。

# test.py文件
class Test(object):
pass
# 创建实例对象
t = Test() # 在其他文件中
from test import t

基于装饰器的实现

def singleton(cls):
instance = None def wrap(*argrs, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance return wrap @singleton
class Test(object): def __init__(self, *args, **kwargs):
pass t1 = Test()
t2 = Test()
print(t1 is t2) # 输出True

基于类(类方法)的实现(这个看别人的博客学来的)

class Test(object):

    def __init__(self):
pass     
@classmethod
def instance(cls, *args, **kwargs):
     # 每次调用此类方法即可创建实例对象
if not hasattr(Test, "_instance"):
Test._instance = Test(*args, **kwargs)
return Test._instance

上述的实现方法在使用多线程时会出现问题,即这种上述实现单例模式不支持多线程

import threading
import time class Test(object): def __init__(self):
time.sleep(2) @classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Test, "_instance"):
Test._instance = Test(*args, **kwargs)
return Test._instance def task(args):
obj = Test.instance()
print(obj) for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()

注意,如果上述不用sleep()暂停,执行速度过快时,仍可能存在相同地址

解决方法:加锁

import threading
import time class Test(object):
_instance_lock = threading.Lock() def __init__(self):
time.sleep(2) @classmethod
def instance(cls, *args, **kwargs):
with Test._instance_lock:
if not hasattr(Test, "_instance"):
Test._instance = Test(*args, **kwargs)
return Test._instance def task(args):
obj = Test.instance()
print(obj) for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start() # 检查长时间后是否为单例
time.sleep(20)
obj = Test.instance()
print(obj)

上述方案长时间后虽然仍为单例模式,但是仍处于加锁的状态,可通过将锁的位置放在判断实例是否存在之后,如果不存在需要重新创建时再加锁

@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Test, "_instance"):
with Test._instance_lock:
Test._instance = Test(*args, **kwargs)
return Test._instance

上述这种方法的问题在于创建实例对象时是通过调用Test.instance()创建的,而不是常规那样创建

基于__new__方法的实现(最常见)

在创建一个实例对象时,先调用__new__方法,再调用__init__方法

import threading
import time
class Test(object): def __init__(self):
time.sleep(1) def __new__(cls, *args, **kwargs):
if not hasattr(Test, "_instance"):
Test._instance = object.__new__(cls) # 相当于继承
return Test._instance obj1 = Test()
obj2 = Test()
print(obj1, obj2) def task(arg):
obj = Test()
print(obj) for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()

也可像之前装饰器一样用一个类属性来判断是否已有创建好的实例对象,如果没有则在__new__中创建,注意必须返回实例对象

这种方法不受线程影响

基于元类方法的实现

python中是类由type创建,在创建时类时会先调用type的__init__方法,然后创建实例对象时会调用type的__call__方法

顺序如下:type的__init__方法(创建类) -> type的__call__方法(类创建实例对象) -> 类的__new__方法(类创建实例对象) -> 类的__init__方法(类初始化实例对象)

元类的使用方式就是写一个继承了type的类,然后在我们所需的类中指定元类(通过metaclass参数),而继承了type的类则可重新定义类的构造函数,单例模式则可在__call__方法中定义

class SingletonType(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance class Test(metaclass=SingletonType):
def __init__(self):
pass obj1 = Test()
obj2 = Test()
print(obj1,obj2)

注意:在元类中__call__的参数cls指所创建的类

python中实现单例模式的更多相关文章

  1. Python学习笔记之在Python中实现单例模式

    有些时候你的项目中难免需要一些全局唯一的对象,这些对象大多是一些工具性的东西,在Python中实现单例模式并不是什么难事.以下总结几种方法: 使用类装饰器 使用装饰器实现单例类的时候,类本身并不知道自 ...

  2. Python中的单例模式

    在 Python 中,我们可以用多种方法来实现单例模式: 使用模块 使用 __new__ 使用装饰器(decorator) 使用元类(metaclass) # mysingleton.py class ...

  3. Python中的单例模式的几种实现方式的优缺点及优化

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  4. Python 中的单例模式

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  5. Python中的单例模式——装饰器实现剖析

    Python中单例模式的实现方法有多种,但在这些方法中属装饰器版本用的广,因为装饰器是基于面向切面编程思想来实现的,具有很高的解耦性和灵活性. 单例模式定义:具有该模式的类只能生成一个实例对象. 先将 ...

  6. python中的单例模式、元类

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  7. Python中的单例模式的几种实现方式的及优化

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  8. python中的单例模式的应用

    1 使用__new__方法 class Singleton(object):    def __new__(cls, *args, **kw):        if not hasattr(cls, ...

  9. Python中的单例模式的几种实现方式和优化以及pyc文件解释(转)

    原文:https://www.cnblogs.com/huchong/p/8244279.html 另一篇关于.pyc文件是什么?  原文: http://blog.sina.com.cn//s/bl ...

随机推荐

  1. shell登陆加载的文件, 快捷命令, tee管道, nohup和&

    1. login shell和nologin shell的理解: 字面意思, 需要登陆的shell和不需要登陆的shell. 正确解释为: 加载用户环境配置的shell 和不加载用户环境配置的shel ...

  2. HDUNumber Sequence(KMP)

    传送门 题目大意:b在a第一次出现的位置 题解:KMP 代码: #include<iostream> #include<cstdio> #include<cstring& ...

  3. Note | 北航《网络安全》复习笔记

    目录 1. 引言 2. 计算机网络基础 基础知识 考点 3. Internet协议的安全性 基础知识 考点 4. 单钥密码体制 基础知识 考点 5. 双钥密码体制 基础知识 考点 6. 消息认证与杂凑 ...

  4. CAS单点登录流程图

    1.cas单点登录原理图 2.cas使用代理服务器流程图 3.cas和spring security集成流程图

  5. 明解C语言 入门篇 第八章答案

    练习8-1 #include<stdio.h> #define diff(x,y)(x-y) int main() { int x; int y; printf("x=" ...

  6. 一次业务网关用ASP.NET Core 2.1重构的小结

    目录 前言 统一鉴权 服务限流 路由转发 参数重组 链路跟踪 熔断降级 服务计次 业务指标监控 日志记录 迭代更新 总结 前言 对于API网关,业界貌似对它进行下划分,有下面几个分类/场景. 面向We ...

  7. 编译安装最新版nettle和gnutls

    编译安装最新版gnutls的时候,总是会出libnettle 3.4.1 was not found的报错信息. 即使编译安装了nettle的最新版3.5之后,依然会报该错. 原因是gnutls编译的 ...

  8. 3、Hibernate的多表关联

    一.数据库中的表关系: 一对一关系 一个人对应一张身份证,一张身份证对应一个人,一对一关系是最好理解的一种关系,在数据库建表的时候可以将人表的主键放置与身份证表里面,也可以将身份证表的主键放置于人表里 ...

  9. 【linux】linux命令lsof和grep命令的配合使用---linux根据端口查看PID,根据PID关键字高亮显示

    lsof命令,根据端口,查看进程PID lsof -i: ps命令+grep命令 --color参数,根据PID查看进程详情,高亮显示关键字 ps -ef | grep --color=always

  10. Zabbix 数据清理

    目录 Zabbix 数据清理的一系列操作 一.问题 二.解决办法 Zabbix 数据清理的一系列操作 基本信息: Zabbix 版本 4.0.9 MySQL 版本 5.5 一.问题 我们将 Zabbi ...