单例模式(Singleton) 核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
单例模式的要点有三个:
- 单例模式的类只提供私有的构造函数
- 类定义中含有一个该类的静态私有对象
- 该类提供了一个静态的共有函数用于创建或获取它本身的静态私有对象。
用途:
- 常用于程序的全局配置;
- 常用于程序引擎类;
- 用于数据的存储,保证数据的唯一性;
- 一个程序中只有一个日志输出实例
优点:
1、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
2、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点:
1、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
2、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
3、对象生成期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致单例类中出现悬浮引用。
如果cls._instance为None,则说明该类还没有被实例化过,new一个该类的实例,并返回;如果cls._instance不为None,直接返回_instance,代码如下:
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance'):
orig=super(Singleton,cls)
cls._instance=orig.__new__(cls,*args,**kwargs)
return cls._instance
class MyClass(Singleton):
a=1
one=MyClass()
two=MyClass()
# one和two完全相同,可以用id(),==,is检查
print(one.a) # 1
print(id(one)) # 2565285375728
print(id(two)) # 2565285375728
print(one == two) # True
print(one is two) # True
二、使用metaclass(元类)的python高级用法
"""
class Singleton中的__init__在Myclass声明的时候被执行Myclass=Singleton()
Myclass()执行时,最先执行父类的__call__方法(object,Singleton都作为Myclass的父类,
根据深度优先算法,会执行Singleton中的__call__(),Singleton中的__call__()写了单例模式)
"""
class Singleton(type):
def __init__(self, name, bases, dict):
super(Singleton,self).__init__(name,bases, dict)
self._instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super(Singleton,self).__call__(*args, **kwargs)
return self._instance
class MyClass(object,metaclass=Singleton):
a = 1
one=MyClass()
two=MyClass()
print(id(one)) # 1553247294800
print(id(two)) # 1553247294800
print(one == two) # True
print(one is two) # True
三、使用python的装饰器(decorator)实现单例模式
这是一种更Pythonic的方法;单例类本身的代码不是单例的,通过装饰器使其单例化。
_instance = {}使用不可变的类地址作为键,其实例作为值,每次创造实例时,首先查看该类是否存在实例,存在的话直接返回该实例即可,否则新建一个实例并存放在字典中。
def singleton(cls, *args, **kwargs):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return _singleton
@singleton
class MyClass3(object):
a = 1
one = MyClass3()
two = MyClass3()
print(id(one)) # 2880466769232
print(id(two)) # 2880466769232
print(one == two) # True
print(one is two) # True
python的单例模式__new__()在__init__()之前被调用,用于生产实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例化一个对象。
class Singleton(object):
__instance=None
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if Singleton.__instance is None:
Singleton.__instance=object.__new__(cls,*args, **kwargs)
return Singleton.__instance
one=Singleton()
two=Singleton()
print(id(one)) # 2488569943376
print(id(two)) # 2488569943376
print(one == two) # True
print(one is two) # True
四、@classmethod实现单例模式
class Singleton:
@classmethod
def getSingletonInstanceObject(cls, *args, **kwargs):
if not hasattr(cls, "ins"):
insObject = cls(*args, **kwargs)
setattr(cls, "ins", insObject)
return getattr(cls, "ins")
if __name__ == "__main__":
ins = Singleton.getSingletonInstanceObject()
print(id(ins))
ins = Singleton.getSingletonInstanceObject()
print(id(ins))
实际应用
假如,我们存储数据工具是SQL Server,我们需要通过host、user、passwd来连接数据库进行读取数据,这时候就需要一次认证,多次调用。
普通模式:
# 一个连接SQL的类
class SqlClient(object):
def __init__(self, host, user, passwd):
self.host = host
self.user = user
self.passwd = passwd
self.register()
def register(self):
self.info = "{}--{}---{}".format(self.host, self.user, self.passwd)
def select(self):
print("SELECT * FROM {}".format(self.host))
SqlClient中有3个方法,__init__用于初始化参数,register是认证SQL客户端,select是执行SQL语句的操作。
后面我们会在不同的地方查找数据,也就是在多个地方需要调用SqlClient类的select方法,有两种方法:1.反复实例化、反复认证,2.把实例化后的对象作为参数传入到每个用到select的函数里。
1.反复实例化、反复认证
host = "10.293.291.19"
user = "admin"
passwd = "666666"
def use_data_1():
sql_client = SqlClient(host, user, passwd)
sql_client.select()
def use_data_2():
sql_client = SqlClient(host, user, passwd)
sql_client.select()
def use_data_3():
sql_client = SqlClient(host, user, passwd)
sql_client.select()
use_data_1()
use_data_2()
use_data_3()
# 输出
SELECT * FROM 10.293.291.19
SELECt * FROM 10.293.291.19
SELECt * FROM 10.293.291.19
在use_data_1、use_data_2、use_data_3三处使用到了SQL选择工具,每一次我们都要重新实例化SqlClient
2.把实例化后的对象作为参数传入到每个用到select的函数里
host = "10.293.291.19"
user = "admin"
passwd = "666666"
def use_data_1(sql_client):
sql_client.select()
def use_data_2(sql_client):
sql_client.select()
def use_data_3(sql_client):
sql_client.select()
sql_client = SqlClient(host, user, passwd)
use_data_1(sql_client)
use_data_2(sql_client)
use_data_3(sql_client)
我们可以先对实例化SqlClient,然后作为参数传入到每一个用到SQL工具的地方。虽然在代码简洁性方面比第一种方法优化了不少,但是传递的参数会很多,在开发中应该尽量少传参数,尤其是链式调用的函数。
单例模式:
class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls)
return cls._instance
class SqlClient(Singleton):
info = None
def register(self, host, user, passwd):
self.info = "{}--{}--{}".format(host, user, passwd)
def select(self):
print(self.info)
通过继承Singleton实现SqlClient的单例模式,我们只需要调用register一次,用于认证客户端,然后后期每次重新实例化都是指向的同一个实例,也就是已经认证过的示例,我们后面任何其他地方调用的地方直接使用select方法即可。
def use_data_1():
SqlClient().select()
def use_data_2():
SqlClient().select()
def use_data_3():
SqlClient().select()
SqlClient().register(host, user, passwd)
use_data_1()
use_data_2()
use_data_3()



