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

前段时间需要用到单例模式,就称着机会在网上找了找,有包含了__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. WPF 精修篇 窗体唯一(Single) 显示在最前

    原文:WPF 精修篇 窗体唯一(Single) 显示在最前 只运行一个窗体 并在一次点击时 显示到最前 发现用 SetForegroundWindow 并不是稳定的有效 最后使用 SetWindowP ...

  2. 2. 词法"陷阱"

    1. 练习2-1 某些编译器允许嵌套注释.请写一个程序测试,要求:无论是对允许嵌套注释的编译器,还是对不允许嵌套注释的编译器,该程序都能正常通过编译,但是这两者情况下执行的结果却不相同. #inclu ...

  3. Java笔记(持续更新)

    码云地址    https://gitee.com/MarkPolaris/Java_Mark

  4. RPA 案例:银行综合对账系统权限更改中的难点解决

    需求内容 根据表格中给出的信息(提供了机构名称.机构代码.用户信息.具体操作等),选择系统管理 → 用户信息管理 → 用户维护,点击用户所在的机构,在机构中选择需调整的用户,进行相应的增删改操作. 关 ...

  5. bootstrap 输入框后面有个按钮

    效果如下:  实现代码:

  6. [IDA] 将变量索引进行计算

    按 k 键 [ebp+var+arg_0] - > [ebp+value]

  7. Abp vNext框架 实例程序BookStore-笔记

    参考 Abp vNext框架 应用程序开发教程 创建项目和书籍列表页面 http://www.vnfan.com/helinbin/d/3579c6e90e1d23ab.html 官方源码 https ...

  8. centOS服务器添加电脑ssh key以支持远程登陆

    1,生成电脑的密钥对(在powershell或cmd命令行中) ssh-keygen -t rsa -C "自己的邮箱" 2.打开刚刚生成的电脑公钥(~即代表用户主目录,/则代表根 ...

  9. 何为http?何为RPC?

    RPC(即Remote Procedure Call,远程过程调用)和HTTP(HyperText Transfer Protocol,超文本传输协议)他们最本质的区别,就是RPC主要工作在TCP协议 ...

  10. git安装和项目上传到GitHub

    重装系统后,Git安装也要来重来 下载 国内下载地址:https://npm.taobao.org/mirrors/git-for-windows 安装忽略 生成密钥 输入然后都是下一步,生成密钥,生 ...