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. Software Engineering homework 3

    博客信息 沈阳航空航天大学计算机学院2020软件工程作业 作业要求 https://edu.cnblogs.com/campus/sau/Computer1701-1705/homework/1061 ...

  2. 2月27日Android开发学习

    App工程目录结构 App工程分为两个层次,第一个层次是项目,另一个层次是模块. 模块依附于项目,每个项目至少有一个模块.一般而言的"编译运行App",指的是运行某一模块,而非运行 ...

  3. 一,创建一个electron应用程序

    之前我们已经用html+css+js创建了一个项目,现在将这个项目用electron以应用程序呈现. 1,首先新建一个文件夹,从终端进入该文件夹: 2,在该文件夹下执行npm init,初始化该项目. ...

  4. 许可协议 :GPL、BSD、MIT、Mozilla、Apache和LGPL

    原文摘自:https://blog.csdn.net/testcs_dn/article/details/38496107 首先借用有心人士的一张相当直观清晰的图来划分各种协议:开源许可证GPL.BS ...

  5. 第12组 Beta冲刺 (1/5)

    1.1基本情况 ·队名:美少女战士 ·组长博客: https://www.cnblogs.com/yaningscnblogs/p/14016591.html ·作业博客:https://edu.cn ...

  6. IT工具知识-13: 如何编辑SVG图像文件并转换为ICO图标文件

    使用背景 最近做了个小软件, 但是桌面快捷方式图标不好看, 于是想着找个好看点的图标, 但是网上搜了一圈, 发现好看的几乎都要钱, 常用的话, 付费倒也不反感, 但是, 仅仅只用那么一两次, 为这个付 ...

  7. (十三).CSS3中的变换(transform),过渡(transition),动画(animation)

    1 变换 transform 1.1 变换相关 CSS 属性 CSS 属性名 含义 值 transform 设置变换方式 transform-origin 设置变换的原点 使用关键字或坐标设置位置 t ...

  8. html:表格

    HTML:表格1.表格三标签:(1)<table> 表格(2)<tr>  行(3)<td>  单元格 2.表格的基本语法和结构<table> <t ...

  9. CentOS7 进入修复模式的办法

    有时候配置Centos文件修改错误会导致系统无法登录,可以通过修复模式进行单用户运行模式,进行修复. 具体操作如下: 1.重启服务器,在选择内核界面使用上下箭头移动 2.选择内核并按"e&q ...

  10. 单向链表&有关类和对象

    // Test515.cpp: 定义控制台应用程序的入口点.// #include "stdafx.h"#include <iostream>using namespa ...