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中实现单例的几种方式的更多相关文章

  1. Python中的单例设计

    01. 单例设计模式 设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码.让代码更容易被他人理解.保 ...

  2. 转--python 中写单例

    原文地址 原文地址2 Python中的单例模式的几种实现方式的及优化 阅读目录(Content) 单例模式 实现单例模式的几种方式 1.使用模块 2.使用装饰器 3.使用类 4.基于__new__方法 ...

  3. Objective-C和Swift实现单例的几种方式

    在Swift开发中,我们对于跨类调用的变量常量,因为并没有OC中使用的全局头文件中写宏的形式,我们一般采用在类外定义全局变量/常量的形式来跨类调用.而问题在于目前写的项目需要在新添加的OC写的功能模块 ...

  4. python中的单例

    使用__new__ 因为一个类每一次实例化的时候,都会走它的__new__方法.所以我们可以使用__new__来控制实例的创建过程,代码如下: class Single: instance = Non ...

  5. .NET Core中延迟单例另一种写法【.NET Core和.NET Framework的beforefieldinit差异】

    1.BeforeFieldInit是什么 前段时间在反编译代码时无意间看到在类中有一个BeforeFieldInit特性,处于好奇的心态查了查这个特性,发现这是一个关于字段初始化时间的特性[提前初始化 ...

  6. Python中的单例设计模式

    1)设计模式: 是前人工作的总结和提炼.通常,被人们广泛流传的设计模式.     某一问题的特定解决方案,使用设计模式是为了可重用代码,是代码更容易被人理解, 增加代码的可用性. 2)单例设计模式: ...

  7. swift实现单例的四种方式

    单例模式 单例模式是设计模式中最简单的一种,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象. 当你只需要一个实例的时候需要使用单例 ...

  8. Java实现单例的5种方式

    1. 什么是单例模式 单例模式指的是在应用整个生命周期内只能存在一个实例.单例模式是一种被广泛使用的设计模式.他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存. 2. 单例模 ...

  9. python 中增加css样式的三种方式

    增加css样式的三种方式: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  10. python中获取文件路径的几种方式

    # 如果执行文件为E:\aa\bb\aa.py 1.获取当前路径 current_path11 = os.path.abspath(__file__) current_path12 = os.path ...

随机推荐

  1. 【python_PAT_乙类】1013_数素数 ,Python运行超时解决方案

    题目: 令 P​i​​ 表示第 i 个素数.现任给两个正整数 M≤N≤10​4​​,请输出 P​M​​ 到 P​N​​ 的所有素数. 输入格式: 输入在一行中给出 M 和 N,其间以空格分隔. 输出格 ...

  2. Bug的前后台分类及定位技巧

    必备工具:Firefox debug工具 一般浏览器F12即可   如何区分页面的bug问题归属:前端or后端 前端bug主要分为3个类别:HTML,CSS,Javascript三类问题 给个最大的区 ...

  3. 关于Python文件读取时,默认把\r\n换成\n

    Python在非二进制形式读取文件时,自动把\r\n换成\n.(window下换行是\r\n) 建立一个test1.txt文件, aaaa bbbb 1.在utf8方式下读取 读取四个字符 1 f=o ...

  4. k8s重启应用

    [root@k8s-master01 opt_k8s]# cat app_list xxx-supervise-srv xxx-recon-srv xxx-mkt-strategy-srv xxx-u ...

  5. @Resource 和@Autowired注解

    @Autowired注解是根据属性进行注入,例如BaseDAO,BaseDAOImpl继承BaseDAO,可以根据BaseDAO类型进行注入 @Resource 注解是根据属性和名称进行注入,比如Ba ...

  6. linux 获取文件名

    https://blog.csdn.net/liuyuedechuchu/article/details/123778605

  7. Sublime Text3汉化好的绿色免安装版使用和破解教程+下载链接

    这个资源是我从官网上面下载好的Sublime Text3绿色版,然后通过网上的教程汉化之后再压缩的.理论上下载完之后,解压就可以用了,不过由于是绿色版,可能需要调一下默认打开方式.才好直接双击打开.绿 ...

  8. 更改windows powershell默认路径

    查看配置文件位置 $profile 修改配置文件 code $profile 加上 Set-Location C:\Users\zzy81\Desktop

  9. django orm的增删改查 以及django1.x和2.x的区别

    ORM对字段的增删改查 # 建一个作者表 class Author(models.Model): ''' 如果你以后在创建表的时候,主键就叫id名,那么可以省略不写,orm会自动帮你创建出主键名称为i ...

  10. yum随笔

    1.一般企业的服务器都能联网,所以装包都是装系统给的或者是自己去第三方的源,如下: http://mirrors.aliyun.com/repo/Centos-7.repo http://mirror ...