zoukankan      html  css  js  c++  java
  • Python

    单例模式

    这是一种设计模式

    • 设计模式是前任工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
    • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

    单例设计模式

    • 目的:让某一个类创建的实例对象,在整个应用程序中只有唯一的一个实例对象而且该对象易于外界访问,从而方便对实例个数的控制并节约系统资源
    • 每一次执行 类名() 返回的对象,内存地址是相同的

    单例设计模式的应用场景

    • 音乐播放器对象
    • 回收站对象
    • 打印机对象
    • .....

    为什么要单例模式?

    • 提问:如何保证一个类只有一个实例并且这个实例易于被访问呢?
    • 不使用单例模式:定义一个全局变量可以确保对象随时都可以被访问,但不能防止实例化多个对象
    • 单例模式的出现:类自己负责只能创建一个实例对象,可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法 

    __new__ 方法

    使用 类名() 创建对象时,Python 的解释器首先会调用 __new__ 方法为对象分配内存空间

    class PoloBlog:
        def __new__(cls, *args, **kwargs):
            print("分配内存地址啦")
    
        def __init__(self):
            print("初始化对象...")
    
    
    blog = PoloBlog()
    print(blog)
    
    
    # 输出结果
    分配内存地址啦
    None

    哎,为什么打印对象是 None,而且没有调用到 __init__ 方法呢??下面讲解!

    内置的静态方法

    __new__ 是一个由 object 基类提供的内置的静态方法

    __new__ 主要作用

    • 在内存中为实例对象分配空间
    • 返回对象的引用给 Python 解释器

    Python 的解释器获得对象的引用后,将对象的引用作为第一个参数,传递给 __init__ 方法

    重写 __new__ 方法

    • 重写的代码是固定的
    • 重写 __new__ 方法一定要在最后 return super().__new__(cls) 
    • 如果不 return(像上面代码栗子一样),Python 的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法(__init__)
    • 重点:__new__ 是一个静态方法,在调用时需要主动传递 cls 参数
    class PoloBlog:
        def __new__(cls, *args, **kwargs):
            # 1、自动调用 __new__
            print("分配内存地址啦")
            # 2、为对象分配空间得到的引用赋值给 instance
            instance = super().__new__(cls)
            print(id(instance))
            # 3、返回对象引用给 Python 解释器
            return instance
    
        def __init__(self):
            print("初始化对象...")
            print(id(self))
    
    
    blog = PoloBlog()
    
    
    # 输出结果
    分配内存地址啦
    4363809888
    初始化对象...
    4363809888

    可以看到打印的两个内存地址是同一个哦:证明 __new__ 分配的对象引用的确传给了 __init__ 方法的 self 参数

    __new__ 实现单例模式

    class PoloBlog:
        def __new__(cls, *args, **kwargs):
            print("分配内存地址啦")
            instance = super().__new__(cls)
            return instance
    
        def __init__(self):
            print("初始化对象...")
    
    
    blog = PoloBlog()
    blog1 = PoloBlog()
    
    print(id(blog))
    print(id(blog1))
    
    
    # 输出结果
    4449363040
    4449361984

    很明显,两个对象各有自己的内存地址;单纯的重写 __new__ 方法并不能实现单例模式

    __new__ 实现单例模式的逻辑

    单例:在整个应用程序中只有唯一的一个实例对象

    1. 定义一个类属性,来保存单例对象的引用
    2. 重写 __new__ 方法
    3. 如果类属性 is None,则调用父类方法分配内存空间,并赋值给类属性
    4. 如果类属性已有对象引用,则直接返回

     

    单例模式的代码实现

    # 单例模式
    class PoloBlog:
        instance = None
    
        def __new__(cls, *args, **kwargs):
            # 1、判断类属性是否为 None
            if cls.instance is None:
                # 2、为空,调用父类方法,给对象分配内存空间,并赋值给类属性
                cls.instance = super().__new__(cls)
    
            # 3、如果不为空,则直接返回类属性保存的对象引用
            return cls.instance
    
        def __init__(self):
            pass
    
    
    blog = PoloBlog()
    blog1 = PoloBlog()
    blog2 = PoloBlog()
    print(id(blog), id(blog1), id(blog2))
    
    
    # 输出结果
    4336982096 4336982096 4336982096

    可以看到创建的三个实例对象其实都是同一个,这就是单例模式!

    初始化工作仅执行一次

    在每次使用类名()创建对象时,Python 的解释器都会自动调用两个方法

    • __new__ 分配空间
    • __init__ 对象初始化

    上面所说的单例模式,是针对 __new__ 方法进行重写的,创建多个实例对象都会得到同一个实例对象

    但是:初始化方法还是会被多次调用

    class PoloBlog:
        instance = None
    
        def __new__(cls, *args, **kwargs):
            if cls.instance is None:
         
                cls.instance = super().__new__(cls)
    
            return cls.instance
    
        def __init__(self):
            print("yep")
    
    
    blog = PoloBlog()
    blog1 = PoloBlog()
    blog2 = PoloBlog()
    
    
    # 输出结果
    yep
    yep
    yep

    假设想让初始化动作只执行一次呢?

    其也很简单,和单例模式的解决思路差不多

    1. 定义一个类属性标记是否执行过初始化动作,初始值为 False
    2. 在 __init__ 方法中,判断类属性,如果 False,则执行初始化动作,然后设置为 True
    3. 如果 True 则直接跳过不执行
    # 单例模式
    class PoloBlog:
        instance = None
        init_flag = None
    
        def __new__(cls, *args, **kwargs):
            if cls.instance is None:
     
                cls.instance = super().__new__(cls)
    
            return cls.instance
    
        def __init__(self):
            # 1、判断是否为 True,因为是实例方法,所以调用类属性要通过类对象
            if PoloBlog.init_flag:
                # 2、如果 True,直接跳过不执行后续初始化动作
                return
            # 3、如果 False,则执行
            print("初始化动作")
            # 4、修改 init_flag
            PoloBlog.init_flag = True
    
    
    blog = PoloBlog()
    blog1 = PoloBlog()
    blog2 = PoloBlog()
    
    
    # 输出结果
    初始化动作
  • 相关阅读:
    MVC中的SelectList函数使用笔记
    如何用JQuery将View中的值Post到Controller
    几种主流网页开发语言的思考(上)
    郁闷的OpenPOP的MIME Parser
    关于OpenPOP/OpenSMTP/Mail.Net的一些东西……
    一个不知道是不是很笨的MS SQL数据库连接类
    Ruby on Rails 细节中的精彩
    关于FreeTextBox 1.63/2.0.7/3.0RC/3.0的一些东西
    翻译:微软是如何输掉API之战(上)
    MSN Messenger的用户管理
  • 原文地址:https://www.cnblogs.com/poloyy/p/15236309.html
Copyright © 2011-2022 走看看