python单例模式控制成只初始化一次,常规型的python单例模式在新式类和经典类中的区别。
单例模式的写法非常多,但常规型的单例模式就是这样写的,各种代码可能略有差异,但核心就是要搞清楚类属性 实例属性,就很容易写出来,原理完全一模一样。
如下:
源码:
class A(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '__inst'): print('执行new') obj = super(A, cls).__new__(cls) setattr(cls, '__inst', obj) return obj else: return cls.__dict__['__inst'] def __init__(self, x): print('执行init') self.x = x if __name__ == '__main__': a1 = A(1) print('a1.x ', a1.x) a2 = A(2) print('a2.x ', a2.x) print('a1.x ', a1.x) a3 = A.__new__(A) # a3.__init__(3) # a3 = A(3) 实际是调用了new和init方法,此处屏蔽调用init print('a3.x ', a3.x) print(id(a1), id(a2), id(a3))
实例化了三个对象,执行结果可以猜猜:
可以发现,执行了一次new,但执行了两次init,这是在新式类下运行的,python3默认是新式类,不管有没有继承object。
如果是python2,且不继承object,实际上是只会打印执行一次init。所以这是py2和py3的又一个区别,经典类和新式类区别非常多,新式类的反射方法也与经典类有些不同。但一般文章只说新式类和经典类的区别只是广度优先和深度优先,误导。
3、终极目标就是使python3的实例也不多执行力一次init,(因为虽然单例模式能控制成是所有类的实例指向同一个对象,但有时候的单例模式初始化是建立一个io连接或者资源池,这样每次执行初始化浪费一些时间)
三种方法,一种是增加一个类属性做标志,在init方法中增加if判断
第二种是,不使用 a = A(xxxx),而使用a = A.__new__(A),因为a = A(xxxx),实际上是a = A.__new__(A),和a.__init__(xxx)
第三种是自己写一个名字例如叫 _custom_init的方法,不写__init__方法,在new中生成self(就是实例),new里面显式手动调用此方法,这样也可以避免自动执行__init__
4、还有一种方式是使用装饰器,如果按照这个写法也不会执行多次init
单例模式装饰器
#coding=utf8 from functools import wraps def singleton(cls): print 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 m1=MyClass() m2=MyClass() print m1 is m2
或者元类单例模式,
单例模式元类
5、在实际项目中我用的单例模式有一些,不是很多,享元模式就可以了,享元模式是对一个重点属性在不同对象中保证该属性是指向同一个对象,使用更为灵活,这个重点属性通常是一个io连接或资源池,或者计算代价十分大的东西,以免造成实例化浪费巨大的时间。