借单例模式掌握python这几个语言要点
创始人
2024-03-21 23:43:15
0

前言

单例模式是常用的设计模式,在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)

方法二:用__new__来实现单例

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的__call__实现单例

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)

总结一下

  • _init_(),对象实例化后被调用
  • _new_(),对象创建时被调用
  • _del_(), 对象析构时被调用
  • _call_(), 对象以"对象()"形式调用被调用,多用在一些框架中,起到动态调用的作用
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__在框架中的使用

一个对象实现了__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有多种多样的构造方式,它们在初始化阶段创建好,然后暂时不用,需要的时候再用。

相关内容

热门资讯

海南逐步成为全球国际商事纠纷解... 本报讯(记者赖书闻)12月25日,记者从海南省政府获悉,海南作为全国5个国际商事仲裁中心建设试点地区...
律师进阶:直面对金钱的喜爱 律师这个职业,已经从光鲜亮丽的精英群体逐步走向了平常职业。但是想要做好律师,难度却一点也没减少。 律...
公安部有关部门下发通知要求 依... 本报北京12月26日讯 记者张晨 2026年元旦、春节将至,节令食品和假期餐饮进入消费高峰期。为切实...
重庆建工集团股份有限公司 关于... 本公司董事会及全体董事保证本公告内容不存在任何虚假记载、误导性陈述或者重大遗漏,并对其内容的真实性、...
产能闲置vs退役潮来袭:动力电... 来源:财联社 中国新能源汽车市场连续多年的高速增长,正将动力电池回收产业推至一个关键的十字路口。 财...
*ST建艺[002789]关于... 本版导读 2025-12-27 2025-12-27 2025-12-27 2025...
诺普信定增与减持并行 年内诉讼... 【深圳商报讯】(记者 詹钰叶)深圳诺普信作物科学股份有限公司(下称诺普信)最近连发两条关于实际控制人...
释新闻|美国在公海扣押委内瑞拉... 继在加勒比海域集结大批军力并对涉嫌运送毒品的船只进行打击之后,特朗普如今又把目标对准了油轮。自12月...
亿晶光电科技股份有限公司关于累... 本公司董事会及全体董事保证本公告内容不存在任何虚假记载、误导性陈述或者重大遗漏,并对其内容的真实性、...
重庆四方新材股份有限公司 关于... 证券代码:605122 证券简称:四方新材 公告编号:2025-080 重庆四方新材股份有限公司 关...