单例模式是常用的设计模式,在Python里有多种实现方式,这些实现方式灵活地利用了python的各种语言特点,深入研究一下,能帮你大大提升python的语言功力。
def singleton(class_):instances = {}def getinstance(*args, **kwargs):if class_ not in instances:instances[class_] = class_(*args, **kwargs)return instances[class_]return getinstance@singleton
class MyClass(BaseClass):pass
def test():x = MyClass();y = MyClass();t = type(n)();assert id(x) == id(y)assert id(x) != id(t) && id(y) != id(t)
python的类变量相当于static变量,只存一份,所以可以利用它来实现单例。
class Singleton(object):_instance = Nonedef __new__(class_, *args, **kwargs):if not isinstance(class_._instance, class_):class_._instance = object.__new__(class_, *args, **kwargs)return class_._instanceclass MyClass(Singleton, BaseClass):pass
分析:重载一个类的__new__方法能接管对象的创建过程。多个实例都会共享同一份_instance。
注意:Python的类变量并不是线程安全的。
metaclass是什么?其实相当于javascript中的prototype(原型)对象,作用就是你可以扩充一个已存在类的方法,如给标准类string加方法。
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(BaseClass):__metaclass__ = Singleton#Python3
class MyClass(BaseClass, metaclass=Singleton):pass
__call__该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。
def test_myclass():obj1 = MyClass() ## 会调用__call__方法obj2 = MyClass() ## 会再次调用__call__方法assert id(obj1) == id(obj2)
def singleton(cls):class class_w(cls):_instance = Nonedef __new__(cls, *args, **kwargs):if class_w._instance is None:class_w._instance = super(cls, cls).__new__(cls, *args,**kwargs)class_w._instance._sealed = Falsereturn class_w._instancedef __init__(self, *args, **kwargs):if self._sealed:returnsuper(class_w, self).__init__(*args, **kwargs)self._sealed = Trueclass_w.__name__ = class_.__name__return class_w@singleton
class MyClass(BaseClass):pass
解释:创建每个对象时, new()被调用,_sealed第一次调用时由False变为True,后续调用不再执行super(class_w, self).init(*args, **kwargs)
class CoolClass():def __init__(self, *args, **kwargs):print('CoolClass is initialized.')for k,v in kwargs.items():print(f"__init__: {k} = {v}")#def __new__(cls, *args, **kwargs):for k,v in kwargs.items():print(f"__new__: {k} = {v}")return super(CoolClass, cls).__new__(cls)def __call__(cls, *args, **kwargs):for k, v in kwargs.items():print(f"__call__: {k} = {v}")def test_object():obj = CoolClass(username='haha', passwd='xixi')obj(username='hoho', passwd='hihi')
结果是先调用__new__(),再调用__init__(),最后调用__call__()。
一个对象实现了__call__()方法,就变成了一个callable对象。有啥用处呢?就是在一些大型框架中,你可以先把这些callable对象创建好,然后在合适的时机调用它们,这给一些设计模式的实现带来了方便。还原一下就是下面的用法:
class CallableClass1:def __call__(self, *args, **kwargs):print('I am class1')class CallableClass2:def __call__(self, *args, **kwargs):print('I am class2')class CallableClass3:def __call__(self, *args, **kwargs):print('I am class3')def test_callable():objs = []objs.append(CallableClass1())objs.append(CallableClass2())objs.append(CallableClass3())for obj in objs:if callable(obj):obj()
实际场景中,CallableClass1~3有多种多样的构造方式,它们在初始化阶段创建好,然后暂时不用,需要的时候再用。