单例模式的几种实现方式

先来看几个魔法方法的简单运用:__new__, __init__, __call__。

class A(object):
def __init__(self, x):
print('x in __init__', x) def __new__(cls, y): # 它只取下 cls 参数,并把其他参数传给 __init__
print('y in __new__', y)
return super(A, cls).__new__(cls) def __call__(self, z): # 允许一个类的实例像函数一样被调用
print('z in __call__', z) A('123')('abc') # 相当于先 a = A('123'), 再 a('abc')
# y in __new__ 123
# x in __init__ 123
# z in __call__ abc
1._new_(cls, *args, **kw) 方法实现

__new__ 构造方法至少需要一个 cls 参数,实例化时,解释器会自动填入;

需要注意的是,new 方法中调用 new 方法时不要再调用自己的 new 方法,会报【超出最大递归深度】错误;要调用父类的 new 方法(默认调用)。

class Singleton(object):
_instance = None # 新建一个类属性:需要使用类名前缀来访问 def __new__(cls, *args, **kw): # 创建实例时,判断是否已经存在,存在,返回,不存在,创建
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kw) # 不要调用自己的new方法, 即: cls.__new__(), 会无限递归的; return cls._instance # 返回实例对象 def __init__(self):
pass sing1 = Singleton()
sing2 = Singleton()
sing1 is sing2 # true """
若 _instance = {}, 则
if cls not in cls._instance:
cls._instance[cls] = object.__new__(cls, *args, **kw)
"""
2.函数装饰器实现:实例化前先调用这个装饰器返回的 inner() 函数,再返回实例

用不可变的 类地址 作为键,其 实例 作为值,每次创造实例时,首先判断该类是否存在实例,存在,直接返回该实例即可,否则新建一个实例并存放在字典中。

def singleton2(cls):
_instance = {} # 新建空字典; 不要_instance = None, 否则 inner 函数中赋值时,_instance 只会在函数作用域搜索,判断时会报错; def inner(): # 判断是否已有实例,如无,则新建一个实例并返回
if cls not in _instance:
_instance[cls] = cls() return _instance[cls] # 返回的是实例对象 return inner @singleton2
class Cls():
def __init__(self):
pass # 可以照常添加属性和方法 cls1 = Cls()
cls2 = Cls()
cls1 is cls2 # True
3.类装饰器实现:类装饰器可以直接依靠类内部的__call__方法来实现

Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及类实例对象。

__call__ 方法可以使类实例对象可以像调用普通函数那样,以 “对象名()” 的形式使用;此方法会在实例作为一个函数被 “调用” 时被调用;

如果定义了此方法,则 x(arg1, arg2, ...) 就相当于 x.__call__(arg1, arg2, ...) 的快捷方式。只要实现了__call__方法的对象都是可被调用对象。

class Singleton3(object):  # 自定义类装饰器
def __init__(self, cls): # 赋初值
self.cls = cls
self.instance = {} def __call__(self): # 调用类装饰器时, 判断是否已存在
if self.cls not in self.instance:
self.instance[self.cls] = self.cls() return self.instance[self.cls] # 返回实例对象 @Singleton3
class Cls2(object):
def __init__(self):
pass cls3 = Cls2()
cls4 = Cls2()
cls3 is cls4 # True class Cls3():
pass Cls33 = Singleton3(Cls3) # Cls3 传给 __init__ 了; cls5 = Cls33() # “名称()” 可以理解为是 “名称.__call__()” 的简写;
cls6 = Cls33()
cls5 is cls6 # True
4.metaless 元类实现

元类->类->实例,简单来说,就是类是由元类创建的,type 是内置的元类,也可以自定义元类。

type(name, bases, dict):name 是类的名字,bases 是要继承的父类集合,dict 是这个类的属性。下面两条语句会创建相同的 type 对象:

class X: a = 1

X = type('X', (object,), dict(a=1)) # 动态的创建类对象,dict(a=1) 也可以直接用 {'a':1, 'func_name': func_name}

class Singleton4(type):
_instances = {} def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton4, cls).__call__(*args, **kwargs) return cls._instances[cls] class Cls4(metaclass=Singleton4):
pass cls1 = Cls4()
cls2 = Cls4()
cls1 is cls2 # True
5.import 实现

python 模块是天然的单例模式:需要使用实例的时候,在文件中导入即可使用,从而减少相关实例的创建;

模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码;

因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

# my_singleton.py
class My_Singleton(object):
def foo(self):
print('My_Singleton') mysingle = My_Singleton() # other.py
from my_singleton import mysingle mysingle.foo()
6.共享属性

创建实例时, 把所有实例的 __dict__ 都指向同一个字典,这样它们就会共享同一个属性,具有相同的属性和方法。

要查看对象中存储的所有值,可检查其 __dict__ 属性。如果要确定对象是由什么组成的,应研究模块 inspect。

注意:此时只是对象值相同,多个对象共享一个数据,依旧以最新的值为准,但不是同一个对象。

class Borg(object):
_state = {} # 共享属性字典 def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state # 指向同一个字典 return ob class MyClass2(Borg):
a = 1
应用场景简介

每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;

当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。

模块 logger 就是一个单例模式;Windows的资源管理器是一个单例模式;线程池,数据库连接池等资源池一般也用单例模式;网站计数器;游戏场景管理器;

游戏中需要有 “场景管理器” 这样一种东西,用来管理游戏场景的切换、资源载入、网络连接等等任务。

这个管理器需要有多种方法和属性,在代码中很多地方会被调用,且被调用的必须是同一个管理器,否则既容易产生冲突,也会浪费内存资源。

某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,

也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。

Python 单例模式的几种实现方式的更多相关文章

  1. python 单例模式的四种创建方式

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  2. JAVA中单例模式的几种实现方式

    1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...

  3. python中的三种输入方式

    python中的三种输入方式 python2.X python2.x中以下三个函数都支持: raw_input() input() sys.stdin.readline() raw_input( )将 ...

  4. Python中的单例模式的几种实现方式的优缺点及优化

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  5. Python中的单例模式的几种实现方式的及优化

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  6. python单例模式的几种实现方法

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  7. Python基础:Python运行的两种基本方式

    完成Python的安装之后,我们可以开始编写Python代码以及运行Python程序了.我们来看一下运行Python具体有哪几种方式 1.REPL 所谓REPL即read.eva.print.loop ...

  8. Python 45 css三种引入方式以及优先级

    一:css三种引入方式 三种方式为:行间式 | 内联式 | 外联式 行间式   1.在标签头部的style属性内  2.属性值满足的是css语法  3.属性值用key:value形式赋值,value具 ...

  9. CSIC_716_20191129【 单例模式 的五种实现方式】

     单例模式 单例模式:在确定类中的属性和方法不变时,需要反复调用该类的情况.  让所有通过该类实例化出的对象,都指向同一个内存地址. 优点:节省内存空间. 单例模式有五种表现形式: 1.通过class ...

随机推荐

  1. SpringCloud之使用Zookeeper作为注册中心

    SpringCloud之使用Zookeeper作为注册中心 linux安装zookeeper 安装zookeeper 关闭linux防火墙 启动zookeeper 1 创建项目导入依赖和配置文件 &l ...

  2. gin框架中使用jwt

    生成解析token 如今有很多将身份验证内置到API中的方法 -JSON Web令牌只是其中之一.JSON Web令牌(JWT)作为令牌系统而不是在每次请求时都发送用户名和密码,因此比其他方法(如基本 ...

  3. IDEA设置Maven

    1,在idea中设置maven,让idea和maven结合使用 idea中内置了maven,一般不使用内置,因为用内置修改maven的设置不方便 使用自己安装的maven,需要覆盖idea中默认的设置 ...

  4. 感恩陪伴 HelloGitHub 定制的红包封面

    距离放假越来越近了,我们更文的频率也越来越低了. 先别打!听我解释... 我真没偷懒,我是去研究今年的「微信红包封面」玩法了. 这不去年,我们制作的 HelloGitHub 专属红包封面,很多粉丝都说 ...

  5. How to find out which process is listening upon a port

    When we covered port scanning a short while ago we discovered how to tell which ports had processes ...

  6. Arduino+ESP32 之 驱动GC9A01圆形LCD(一),基于Arduino_GFX库

    最近买了一块圆形屏幕,驱动IC是GC9A01,自己参考淘宝给的stm32的驱动例程, 在ubuntu下使用IDF开发ESP32,也在windows的vscode内安装IDF开发ESP32,虽然都做到了 ...

  7. AtCoder Beginner Contest 238 A - F 题解

    AtCoder Beginner Contest 238 \(A - F\) 题解 A - Exponential or Quadratic 题意 判断 \(2^n > n^2\)是否成立? S ...

  8. 使用gige2500万相机时遇见的问题(条纹以及取图过久)

    1.确保网卡支持1g全双工: 2.确保安装了MVTec GigE Vision Streaming Filter(使用halcon接口的情况下) 3.确保机台的杀毒软件开放了相机的网络监控功能

  9. UNIX系统上的抓包工具tcpdump常用命令说明

    tcpdump 介绍 tcpdump采用命令行方式对接口的数据包进行筛选抓取,其丰富特性表现在灵活的表达式上. 不带任何选项的tcpdump,默认会抓取第一个网络接口,且只有将tcpdump进程终止才 ...

  10. 一个好用的多方隐私求交算法库JasonCeng/MultipartyPSI-Pro

    Github链接传送:JasonCeng/MultipartyPSI-Pro 大家好,我是阿创,这是我的第29篇原创文章. 今天是一篇纯技术性文章,希望对工程狮们有所帮助. 向大家推荐一个我最近改造的 ...