文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python学习笔记:单例模式

2023-01-30 22:35

关注

单例模式:一个类无论实例化多少次,返回的都是同一个实例,例如:a1=A(), a2=A(), a3=A(),a1、a2和a3其实都是同一个对象,即print(a1 is a2)和print(a2 is a3)都会打印True。

实现方式:有两种方式,一种是使用元类metaclass控制类实例化时的对象,另一种是使用类的__new__方法控制类返回的对象,推荐使用元类的方式,因为__new__通常是用来改变类结构的。

注:关于元类和单例模式,本文只是贴了两个简单的示例代码和自己的一些心得,想要更加深入的学习,这里有一篇博客讲得很详细https://www.cnblogs.com/tkqasn/p/6524879.html

 

元类实现单例模式(Python3.6):

 1 class Singleton(type):
 2     def __init__(cls, *args, **kwargs):
 3         cls.__instance = None
 4         super().__init__(*args, **kwargs)
 5 
 6     def __call__(cls, *args, **kwargs):
 7         if cls.__instance is None:
 8             cls.__instance = super().__call__(*args, **kwargs)
 9 
10         return cls.__instance
11 
12 
13 class MySingleton(metaclass=Singleton):
14     def __init__(self, val):
15         self.val = val
16         print(self.val)
17 
18 
19 hello = MySingleton('hello')
20 hi = MySingleton('hi')
21 print(hello is hi)
22 
23 ----------输出结果----------
24 hello
25 True
  • metaclass:Python3中metaclass是通过指定metaclass实现的,Python2中是通过指定类变量__metaclass__来实现的,但原理都是一样的。
  • type:Python中所有的类都是type类的实例,即一个类(还未实例化)的定义,其实就是type类(Python内建元类)的实例。如下的打印可以更加直观的理解这一点:
    >>> int.__class__
    <class 'type'>
    >>> num = 3
    >>> num.__class__
    <class 'int'>
    >>> num.__class__.__class__
    <class 'type'>
    >>> 
    >>> 
    >>> class A:
        pass
    
    >>> A.__class__
    <class 'type'>
    >>> a = A()
    >>> a.__class__
    <class '__main__.A'>
    >>> a.__class__.__class__
    <class 'type'>
    >>> 

     

  • __call__:当调用一个实例时,即执行实例加括号的形式,就会调用该实例的__call__方法,如果没有定义(需要自己定义),则会报错。例如a=A(),a()就会调用a的__call__方法。
  • 代码执行流程:第一步执行MySingleton时(即没加括号的部分),进行元类的实例化,即MySingleton=Singleton(),Singleton的实例化和普通类一样会先执行__new__返回该类的实例,然后自动执行该实例的__init__方法进行初始化,此示例中的初始化方法给该实例赋予了一个值为None的__instance变量;第二步执行MySingleton('hello')时,进行类的实例化,即MySingleton('hello')=Singleton()('hello'),这里就会调用到Singleton的__call__方法了,而super().__call__即调用type的__call__方法,这时候就和普通类实例化一样会调用MySingleton的__new__和__init__方法了。
  • 原理:由于每次实例化MySingleton时都会先调用metaclass中的__call__方法,所以只有第一次实例化时才会执行MySingleton的__new__和__init__,后面的实例化都只会返回第一次实例化好的实例,所以导致的结果就是无论进行多少次实例化,都给你返回同一个实例,当然就只有单例了(所以“输出结果”中就没有打印“hi”了) 。
  • cls和self:Singleton的编写,在eclipse中提示需要写成self,在PyCharm中提示需要写成cls,因为参数self是约定代表实例本身,但在这里type的实例就是类,所以推荐写成cls。

 

__new__实现单例模式(Python3.6):

 1 class MySingleton:
 2     def __init__(self, val):
 3         self.val = val 
4 print(self.val)
5 print(self.__dict__) 6 7 def __new__(cls, *args, **kwargs): 8 if not hasattr(cls, '_instance'): 9 cls._instance = super().__new__(cls) 10 11 return cls._instance 12 13 14 hello = MySingleton('hello') 15 hi = MySingleton('hi') 16 print(hello is hi) 17 18 19 -----------输出结果-------------- 20 hello
21 {'val': 'hello'}
22 hi
23 {'val': 'hi'}
24 True
  • 原理:通过给类定义一个类变量,指向本类的一个实例,每次实例化调用__new__的时候都返回这个类变量,可以看到数据结果打印的是True,所以自然就是单例了。
  • 缺点:每次实例化虽然都是同一个实例,但是每次实例化都会调用一次__init__方法,导致这个实例会随着每次初始化而改变,所以不推荐这种方式来实现单例,因为__new__方法一般是用来改变类结构的。
  • hasattr:类中的私有变量,即加了双下划线的变量,在__dict__中会加上一个“_classname”前缀,所以如果这里使用__instance的话,hasattr(cls, '__instance')会一直返回False,因为这里已经不是__instance了,而是_MySingleton__instance。

 

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯