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 ...
随机推荐
- Software Engineering homework 3
博客信息 沈阳航空航天大学计算机学院2020软件工程作业 作业要求 https://edu.cnblogs.com/campus/sau/Computer1701-1705/homework/1061 ...
- 2月27日Android开发学习
App工程目录结构 App工程分为两个层次,第一个层次是项目,另一个层次是模块. 模块依附于项目,每个项目至少有一个模块.一般而言的"编译运行App",指的是运行某一模块,而非运行 ...
- 一,创建一个electron应用程序
之前我们已经用html+css+js创建了一个项目,现在将这个项目用electron以应用程序呈现. 1,首先新建一个文件夹,从终端进入该文件夹: 2,在该文件夹下执行npm init,初始化该项目. ...
- 许可协议 :GPL、BSD、MIT、Mozilla、Apache和LGPL
原文摘自:https://blog.csdn.net/testcs_dn/article/details/38496107 首先借用有心人士的一张相当直观清晰的图来划分各种协议:开源许可证GPL.BS ...
- 第12组 Beta冲刺 (1/5)
1.1基本情况 ·队名:美少女战士 ·组长博客: https://www.cnblogs.com/yaningscnblogs/p/14016591.html ·作业博客:https://edu.cn ...
- IT工具知识-13: 如何编辑SVG图像文件并转换为ICO图标文件
使用背景 最近做了个小软件, 但是桌面快捷方式图标不好看, 于是想着找个好看点的图标, 但是网上搜了一圈, 发现好看的几乎都要钱, 常用的话, 付费倒也不反感, 但是, 仅仅只用那么一两次, 为这个付 ...
- (十三).CSS3中的变换(transform),过渡(transition),动画(animation)
1 变换 transform 1.1 变换相关 CSS 属性 CSS 属性名 含义 值 transform 设置变换方式 transform-origin 设置变换的原点 使用关键字或坐标设置位置 t ...
- html:表格
HTML:表格1.表格三标签:(1)<table> 表格(2)<tr> 行(3)<td> 单元格 2.表格的基本语法和结构<table> <t ...
- CentOS7 进入修复模式的办法
有时候配置Centos文件修改错误会导致系统无法登录,可以通过修复模式进行单用户运行模式,进行修复. 具体操作如下: 1.重启服务器,在选择内核界面使用上下箭头移动 2.选择内核并按"e&q ...
- 单向链表&有关类和对象
// Test515.cpp: 定义控制台应用程序的入口点.// #include "stdafx.h"#include <iostream>using namespa ...