使用场景:
1)在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。
继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。
2)网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制
3)对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。
4)发送短信时,多个用户创建实例,可以使用单例
从上述的例子中我们可以总结出适合使用单例模式的场景和优缺点:
适用场景:
-
1.需要生成唯一序列的环境
-
2.需要频繁实例化然后销毁的对象。
-
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
-
4.方便资源相互通信的环境
实现方式
– 使用模块
– 使用__new__
– 使用装饰器
– 使用元类(metaclass)
1)使用模块
Python模块的导入,一个模块创建类并实例化,另一个模块导入,此时多次调用就是单例,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
# mysingle.py
class MySingle:
def foo(self):
pass
sinleton = MySingle()
将上面的代码保存在文件mysingle.py中,然后这样使用:
from mysingle import sinleton
singleton.foo()
2)使用__new__方法
实例化的过程其实不是直接调用init的,首先是new分配一块空间来创建实例,再由init对这个实例进行初始化.我们无法阻止new和init的调用,我们只能是限制他们的内容,以此使他们能达到单例的目的
__author__ = ''
__time__ = '2019/12/25 17:22'
class people(object):
def __new__(cls,*args,**kargs):
return super(people,cls).__new__(cls)
def __init__(self,name):
self.name = name
def talk(self):
print("hello,I am %s" %self.name)
class student(people):
def __new__(cls,*args,**kargs):
if not hasattr(cls,"instance"):
cls.instance = super(student,cls).__new__(cls,*args,**kargs)
return cls.instance
a = student("Timo")
print(a)
b = student("kysa")
c = student("Luyi")
a.talk()
b.talk()
print(c)
这里的输出结果是:
<__main__.student object at 0x0000025AC48BF2E8>
hello,I am Luyi
hello,I am Luyi
<__main__.student object at 0x0000025AC48BF2E8>
可以确定的是: 确实是单例了,因为a的id和b,c的id是一致的
但是为什么:a先创建明明是Timo,可是为什么a的name变成了Luyi呢?
原因:
虽然确实是a这个实例,但是在最后c重新调用了new,返回了a的实例,再经过init,改变了a的属性,执行时name ->Luyi.
解决:
这种情况下,我们只需要设置类变量,让init在类变量的限制下,只对类进行一次有效的初始化.
__author__ = ''
__time__ = '2019/12/25 17:22'
class people(object):
def __new__(cls,*args,**kargs):
return super(people,cls).__new__(cls)
def __init__(self,name):
self.name = name
def talk(self):
print("hello,I am %s" %self.name)
class student(people):
def __new__(cls,*args,**kargs):
if not hasattr(cls,"instance"):
cls.instance = super(student,cls).__new__(cls,*args,**kargs)
return cls.instance
def __init__(self,name):
if not hasattr(self,"init_fir"):
self.init_fir = True
super(student,self).__init__(name)
a = student("Timo")
print(a)
b = student("kysa")
c = student("Luyi")
a.talk()
b.talk()
print(c)
3)使用装饰器
装饰器里面的外层变量定义一个字典,里面存放这个类的实例.当第一次创建的收,就将这个实例保存到这个字典中.
然后以后每次创建对象的时候,都去这个字典中判断一下,如果已经被实例化,就直接取这个实例对象.如果不存在就保存到字典中
# encoding:utf-8
__author__ = ''
__time__ = '2019/12/25 17:22'
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(object):
a = 1
def __init__(self, x=0):
self.x = x
print('这是A的类的初始化方法')
a1 = A(2)
a2 = A(3)
print(id(a1), id(a2))
4)使用metaclass(元类)
元类可以控制类的创建过程,它主要做三件事:
– 拦截类的创建
– 修改类的定义
– 返回修改后的类
class Singleton2(type):
def __init__(self, *args, **kwargs):
self.__instance = None
super(Singleton2,self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton2,self).__call__(*args, **kwargs)
return self.__instance
class Foo(object):
__metaclass__ = Singleton2 #在代码执行到这里的时候,元类中的__new__方法和__init__方法其实已经被执行了,而不是在Foo实例化的时候执行。且仅会执行一次。
foo1 = Foo()
foo2 = Foo()
print (Foo.__dict__) #_Singleton__instance': <__main__.Foo object at 0x100c52f10> 存在一个私有属性来保存属性,而不会污染Foo类(其实还是会污染,只是无法直接通过__instance属性访问)
print (foo1 is foo2) # True
共享属性
创建实例时把所有实例的
__dict__
指向同一个字典,这样它们具有相同的属性和方法.
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