Python中实现单例的几种方式
Python如何实现单例?
什么是单例模式?
单例模式:一个类只能有一个实例化对象存在的模式。
如何实现单例?
1.使用模块
python中模块是天然的单例模式,当一个模块被调用时,会生成对应的.pyc文件,接下来的每次使用都会自动读取.pyc文件里的内容,因此,要使用模块实现单例,只需这样做:
# mysingleton.py
class Singleton:
def fuc(self):
pass
singleton = Singleton()
我们在使用时只需引用该模块的singleton对象就行了
2.使用装饰器
def singleton(cls):
_instance = {}
def _singleton(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]
return _singleton
@singleton
class A:
a = 1 # 类属性
def __init__(self, x):
self.x = x # 类的实例化对象属性
a1 = A(1)
a2 = A(2)
3.使用类
class Singleton:
def __init__(self, *args, **kwargs):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
上述方式实现的单例模式,在单线程时是可行的,但是在多线程时就会出现问题,假设上述的__init__()方法中有一些耗时的io操作,这里我们用sleep模拟一下:
class Singleton:
def __init__(self, *args, **kwargs):
import time
time.sleep(10)
再用多线程执行创建实例化对象的操作:
import threading
def task(arg):
job = Singleton.instance()
print(job)
for i in range(10):
t = threading.Thread(task,args=[i,])
t.start()
执行结果如下
<__main__.Singleton object at 0x7ff873a9c2d0>
<__main__.Singleton object at 0x7ff873b23e10>
<__main__.Singleton object at 0x7ff873b30850>
<__main__.Singleton object at 0x7ff873b30350>
<__main__.Singleton object at 0x7ff873b30710>
<__main__.Singleton object at 0x7ff873b23f50>
<__main__.Singleton object at 0x7ff873b305d0>
<__main__.Singleton object at 0x7ff873b30490>
<__main__.Singleton object at 0x7ff873b30210>
<__main__.Singleton object at 0x7ff873b300d0>
从执行结果中我们可以发现:在多线程情况之下,通过上述方式创建的实例化对象,内存地址并不一样,这也就意味着我们并没有实现单例模式。那么,为什么这种单例模式的写法无法在多线程情况下奏效呢?
在搞清楚这个问题之前,我们需要先了解一点,即:任何数据实例化过程或者数据声明过程都是向系统申请内存空间,这些过程都是io,也即耗时操作。
再回到代码本身,在多线程情况下,当线程1运行完if not,但还没有执行实例化过程,此时如果CPU切换到线程2,线程2也执行到了if not,并完成了实例化过程,然后CPU再切换回了线程1,并继续执行了线程1的实例化过程,那么就是两个线程分别实例化了一次,也就不是单例了(这里还需要了解CPU的切换机制,在此处就不详细阐述了)
搞清楚上述方法无法满足多线程的原因之后,我们再来看如何去解决上述问题:
添加线程锁
import threading
class Singleton:
_instance_lock = threading.Lock()
def __init__(self, *args, **kwargs):
import time
time.sleep(5)
@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
当我们添加了线程锁之后,就可以在多线程情况下实现单例了:
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
<__main__.Singleton object at 0x7f834719c310>
4.基于__new__方法实现(推荐)
实例化对象时,会先执行类的new方法(没有__new__方法时会默认执行object的__new__方法),然后再执行__init__方法初始化实例对象,那么我们可以通过定义__new__方法实现单例
import threading
class Singleton:
_instance_lock = threading.Lock()
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with _instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
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 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
<__main__.Singleton object at 0x7f9f37a9c350>
5.基于metaclass(元类)实现
"""
关于元类(type):这里元类的概念,我们可以理解为创建类的类。当我们创建一个类时,会执行type的__init__()方法,使用类名+()创建类的实例化对像时,会调用元类type的__call__()方法。
关于__call__():该方法的功能类似于在类中重载()运算符,使得类的实例化对象可以像调用普通函数那样,以对象名+()的形式调用,普通类的对象基于类,类基于元类,都是如此。
"""
__call__的使用:
class Foo:
def __call__(self, *args, **kwargs):
print("执行__call__。。。")
obj = Foo()
print("创建实例化对象obj")
obj()
输出结果为:
创建实例化对象obj
执行__call__。。。
利用元类的__call__方法,我们可以这样实现单例:
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):
import time
time.sleep(5)
def task(arg):
obj = Foo()
print(obj)
for i in range(10):
t = threading.Thread(target=task, args=[i,])
t.start()
执行结果为:
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
<__main__.Foo object at 0x7ff725a9cf50>
参考链接:https://www.cnblogs.com/huchong/p/8244279.html
Python中实现单例的几种方式的更多相关文章
- Python中的单例设计
01. 单例设计模式 设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码.让代码更容易被他人理解.保 ...
- 转--python 中写单例
原文地址 原文地址2 Python中的单例模式的几种实现方式的及优化 阅读目录(Content) 单例模式 实现单例模式的几种方式 1.使用模块 2.使用装饰器 3.使用类 4.基于__new__方法 ...
- Objective-C和Swift实现单例的几种方式
在Swift开发中,我们对于跨类调用的变量常量,因为并没有OC中使用的全局头文件中写宏的形式,我们一般采用在类外定义全局变量/常量的形式来跨类调用.而问题在于目前写的项目需要在新添加的OC写的功能模块 ...
- python中的单例
使用__new__ 因为一个类每一次实例化的时候,都会走它的__new__方法.所以我们可以使用__new__来控制实例的创建过程,代码如下: class Single: instance = Non ...
- .NET Core中延迟单例另一种写法【.NET Core和.NET Framework的beforefieldinit差异】
1.BeforeFieldInit是什么 前段时间在反编译代码时无意间看到在类中有一个BeforeFieldInit特性,处于好奇的心态查了查这个特性,发现这是一个关于字段初始化时间的特性[提前初始化 ...
- Python中的单例设计模式
1)设计模式: 是前人工作的总结和提炼.通常,被人们广泛流传的设计模式. 某一问题的特定解决方案,使用设计模式是为了可重用代码,是代码更容易被人理解, 增加代码的可用性. 2)单例设计模式: ...
- swift实现单例的四种方式
单例模式 单例模式是设计模式中最简单的一种,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象. 当你只需要一个实例的时候需要使用单例 ...
- Java实现单例的5种方式
1. 什么是单例模式 单例模式指的是在应用整个生命周期内只能存在一个实例.单例模式是一种被广泛使用的设计模式.他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存. 2. 单例模 ...
- python 中增加css样式的三种方式
增加css样式的三种方式: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
- python中获取文件路径的几种方式
# 如果执行文件为E:\aa\bb\aa.py 1.获取当前路径 current_path11 = os.path.abspath(__file__) current_path12 = os.path ...
随机推荐
- C语言初级阶段7——指针4
C语言初级阶段7--指针4 结构体指针 1.概念:所谓的结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针.如果把一个结构体变量的其实地址存放在一个指针变量中,那么这 ...
- linux 学习之awk
awk 笔记 awk可以截取列 如 ll | awk '{print $3}' 获取第三列内容 参数 -F 指定分隔符 如 ls | wak -F "." '{print $1}' ...
- hdu: Dire Wolf(区间DP)
Problem DescriptionDire wolves, also known as Dark wolves, are extraordinarily large and powerful wo ...
- javaheima14
Java 日志框架 日志框架的概述 记录程序运行过程中的信息,并可以进行永久存储 以前记录日志的方式--输出语句 弊端 信息只能展示在控制台 不能将其记录到其他位置(文件,数据库) 想取消记录的信息需 ...
- C++11:初始化列表
在老版本的C++中,我们可以比较方便得对结构体.数组等对象利用{}进行初始化,而类变量的初始化则取决于构造函数的形式,例如: struct A { int a, b, c; }; class Foo ...
- 盒子模型和CSS背景和列表
盒子模型(1)宽度-width:长度值 | 百分比 | auto-max-width:长度值 | 百分比 | auto-min-width:长度值 | 百分比 | auto(2)高度-height:长 ...
- idea的tomcat控制台输出乱码
tomcat乱码问题 idea的tomcat控制台输出乱码 找到自己的安装目录 用vscode打开 ctrl+f打开搜索 输入encoding 最后一个是用来给idea中的控制台输出 --->改 ...
- hbase 集群写入能力优化-预分区、TTL的应用
一.概述 hbase 写入优化除了参数配置之外,很大的一块要考虑避免region的热点问题,避免region 热点问题,主要的目的是提高hbase 数据表rowkey的分散.结合实际情况主要有以下几个 ...
- 用javassist和CGLIB 解决JDK动态代理的缺陷
用 javassist 解决 JDK 动态代理的缺陷 JDK动态代理的缺陷 要求目标类必须实现接口,否则产生不了代理.有些场景下,目标类也要产生代理类但是木有实现接口,这个时间 Java 中有两个常 ...
- Linux 复制时排除某文件/目录
如果要排除/home/data目录下面的a.b.c.三个目录,同时拷贝其它所有目录,执行rsync命令yum install rsync -y #安装rsync 排除单个文件/目录rsync -avP ...