python单例模式的多种实现方法

作者:谷子的 时间:2023-05-01 12:05:04 

前言

单例模式(Singleton Pattern),是一种软件设计模式,是类只能实例化一个对象,

目的是便于外界的访问,节约系统资源,如果希望系统中 只有一个对象可以访问,就用单例模式,

显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

在 Python 中,我们可以用多种方法来实现单例模式:

  • 使用模块

  • 使用 __new__

  • 使用装饰器(decorator)

  • 使用元类(metaclass)

概念

简单说,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)

例子:

一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例。

super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。

1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象;

2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;

3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;

4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);

5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。

__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)

__init__ : 对象的初始化, 是一个实例方法,第一个参数是self。

__new__方法在类定义中不是必须写的,如果没定义,默认会调用object.__new__去创建一个对象。如果定义了,就是override,可以custom创建对象的行为。

聪明的读者可能想到,既然__new__可以custom对象的创建,那我在这里做一下手脚,每次创建对象都返回同一个,那不就是单例模式了吗?没错,就是这样。可以观摩《飘逸的python - 单例模式乱弹》

定义单例模式时,因为自定义的__new__重载了父类的__new__,所以要自己显式调用父类的__new__,即object.__new__(cls, *args, **kwargs),或者用super()。,不然就不是extend原来的实例了,而是替换原来的实例。

代码


import threading
class Signleton(object):
 def __init__(self):
   print("__init__ method called")
 def __new__(cls):
   print("__new__ method called")
   mutex=threading.Lock()
   mutex.acquire() # 上锁,防止多线程下出问题
   if not hasattr(cls, 'instance'):
     cls.instance = super(LogSignleton, cls).__new__(cls)
   mutex.release()
   return cls.instance
if __name__ == '__main__':
obj = Signleton()

输出结果:


>>> ================================ RESTART ================================
>>>
__new__ method called
__init__ method called
>>>

说明

1.从输出结果来看,最先调用 __new__ 方法,然后调用__init__方法

2. __new__ 通常用于控制生成一个新实例的过程,它是类级别的方法。

3. __init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。

方法1

__new__ 在__init__初始化前,就已经实例化对象,可以利用这个方法实现单例模式。


print '----------------------方法1--------------------------'
#方法1,实现__new__方法
#并在将一个类的实例绑定到类变量_instance上,
#如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
#如果cls._instance不为None,直接返回cls._instance
class Singleton(object):
 def __new__(cls, *args, **kw):
   if not hasattr(cls, '_instance'):
     orig = super(Singleton, cls)
     cls._instance = orig.__new__(cls, *args, **kw)
   return cls._instance

class MyClass(Singleton):
 a = 1

one = MyClass()
two = MyClass()

two.a = 3
print one.a
#3
#one和two完全相同,可以用id(), ==, is检测
print id(one)
#29097904
print id(two)
#29097904
print one == two
#True
print one is two
#True

方法2


print '----------------------方法2--------------------------'
#方法2,共享属性;所谓单例就是所有引用(实例、对象)拥有相同的状态(属性)和行为(方法)
#同一个类的所有实例天然拥有相同的行为(方法),
#只需要保证同一个类的所有实例具有相同的状态(属性)即可
#所有实例共享属性的最简单最直接的方法就是__dict__属性指向(引用)同一个字典(dict)
#可参看:http://code.activestate.com/recipes/66531/
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

one = MyClass2()
two = MyClass2()

#one和two是两个不同的对象,id, ==, is对比结果可看出
two.a = 3
print one.a
#3
print id(one)
#28873680
print id(two)
#28873712
print one == two
#False
print one is two
#False
#但是one和two具有相同的(同一个__dict__属性),见:
print id(one.__dict__)
#30104000
print id(two.__dict__)
#30104000

方法3


print '----------------------方法3--------------------------'
#方法3:本质上是方法1的升级(或者说高级)版
#使用__metaclass__(元类)的高级python用法
class Singleton2(type):
 def __init__(cls, name, bases, dict):
   super(Singleton2, cls).__init__(name, bases, dict)
   cls._instance = None
 def __call__(cls, *args, **kw):
   if cls._instance is None:
     cls._instance = super(Singleton2, cls).__call__(*args, **kw)
   return cls._instance

class MyClass3(object):
 __metaclass__ = Singleton2

one = MyClass3()
two = MyClass3()

two.a = 3
print one.a
#3
print id(one)
#31495472
print id(two)
#31495472
print one == two
#True
print one is two
#True

方法4


print '----------------------方法4--------------------------'
#方法4:也是方法1的升级(高级)版本,
#使用装饰器(decorator),
#这是一种更pythonic,更elegant的方法,
#单例类本身根本不知道自己是单例的,因为他本身(自己的代码)并不是单例的
def singleton(cls, *args, **kw):
 instances = {}
 def _singleton():
   if cls not in instances:
     instances[cls] = cls(*args, **kw)
   return instances[cls]
 return _singleton

@singleton
class MyClass4(object):
 a = 1
 def __init__(self, x=0):
   self.x = x

one = MyClass4()
two = MyClass4()

two.a = 3
print one.a
#3
print id(one)
#29660784
print id(two)
#29660784
print one == two
#True
print one is two
#True
one.x = 1
print one.x
#1
print two.x

单例模式

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

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

在 Python 中,我们可以用多种方法来实现单例模式:

  • 使用模块

  • 使用 __new__

  • 使用装饰器(decorator)

  • 使用元类(metaclass)

使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:


# mysingleton.py
class My_Singleton(object):
 def foo(self):
   pass

my_singleton = My_Singleton()

将上面的代码保存在文件 mysingleton.py 中,然后这样使用:


from mysingleton import my_singleton
my_singleton.foo()

使用 __new__
为了使类只能出现一个实例,我们可以使用 __new__ 来控制实例的创建过程,代码如下:


class Singleton(object):
 _instance = None
 def __new__(cls, *args, **kw):
   if not cls._instance:
     cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
   return cls._instance

class MyClass(Singleton):
 a = 1

在上面的代码中,我们将类的实例和一个类变量 _instance 关联起来,如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。

执行情况如下:


>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862608, 4303862608)

使用装饰器

我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:


from functools import wraps

def singleton(cls):
 instances = {}
 @wraps(cls)
 def getinstance(*args, **kw):
   if cls not in instances:
     instances[cls] = cls(*args, **kw)
   return instances[cls]
 return getinstance

@singleton
class MyClass(object):
 a = 1

在上面,我们定义了一个装饰器 singleton,它返回了一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls 作为 key,cls(*args, **kw) 作为 value 存到 instances 中,否则,直接返回 instances[cls]。

使用 metaclass

元类(metaclass)可以控制类的创建过程,它主要做三件事:

  • 拦截类的创建

  • 修改类的定义

  • 返回修改后的类

使用元类实现单例模式的代码如下:


class Singleton(type):
 _instances = {}
 def __call__(cls, *args, **kwargs):
   if cls not in cls._instances:
     cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
   return cls._instances[cls]

# Python2
class MyClass(object):
 __metaclass__ = Singleton

# Python3
# class MyClass(metaclass=Singleton):
#  pass

来源:https://www.cnblogs.com/gyh04541/p/7853652.html

标签:python,单例,模式,实现
0
投稿

猜你喜欢

  • Python线程threading模块用法详解

    2023-01-30 03:55:01
  • Eclipse+Java+Swing+Mysql实现电影购票系统(详细代码)

    2024-01-16 08:09:21
  • 很有意思的SQL多行数据拼接

    2011-11-03 17:08:29
  • Windows下Apache+MySQL+PHP运行环境的安装图文方法

    2023-10-14 02:05:31
  • Python numpy中的ndarray介绍

    2022-11-06 19:17:59
  • 通过Django Admin+HttpRunner1.5.6实现简易接口测试平台

    2023-05-24 19:07:45
  • php使用pack处理二进制文件的方法

    2023-11-21 04:26:11
  • request基本使用及各种请求方式参数的示例

    2021-03-31 02:32:52
  • PHP addslashes()函数讲解

    2023-06-04 04:28:24
  • OpenCV+python3实现视频分解成图片

    2023-05-30 19:10:55
  • 基于python计算滚动方差(标准差)talib和pd.rolling函数差异详解

    2023-04-09 17:28:45
  • 关于DOM元素定位属性的深入学习

    2008-04-02 11:46:00
  • 发一新浪招聘的图片滚动控制JS效果

    2011-08-10 19:17:25
  • Python应用实现处理excel数据过程解析

    2022-10-24 11:52:17
  • MySQL的 DDL和DML和DQL的基本语法详解

    2024-01-15 06:40:43
  • JS将数字转换成三位逗号分隔的样式(示例代码)

    2024-05-02 16:26:59
  • python的函数和方法(上)

    2021-02-04 11:46:16
  • caffe binaryproto 与 npy相互转换的实例讲解

    2021-10-22 15:38:48
  • Java实现学生信息管理系统(使用数据库)

    2024-01-25 11:22:03
  • Python操作word文档的示例详解

    2021-11-10 02:54:51
  • asp之家 网络编程 m.aspxhome.com