72 单例模式

484次阅读
没有评论

共计 2440 个字符,预计需要花费 7 分钟才能阅读完成。

一. 什么是单例模式

单例模式 (Singleton Pattern) 是一种常用的软件 设计模式 (Deaign pattern), 提供了在软件开发过程中面临的一些问题的最佳解决方案

而单例模式的主要目的是确保某一个类只有一个实例存在, 即一个类多次实例的结果指向同一个对象, 用于节省内存

如果我们从配置文件中读取配置来进行实例化, 在配置相同的情况下, 就没有必要重复产生对象来浪费内存了

二. 单例模式的优缺点

1. 单例模式的优点

  • 由于单例模式要求在全局类只有一个实例, 因而可以节省比较多的内存空间
  • 全局只有一个接入点, 可以进行更好的进行数据同步控制, 避免多重占用
  • 单例可以常驻内存, 减少内存开销

2. 单例模式的缺点

  • 单例模式的扩展比较困难
  • 赋于了单例以太多的职责,某种程度上违反单一职责原则
  • 单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试
  • 单例模式在某种情况下会导致 "资源瓶颈"

三. 单例模式的应用场景示例

  • 生成全局惟一的序列号
  • 访问全局复用的惟一资源,如磁盘、总线等
  • 单个对象占用的资源过多,如数据库等
  • 系统全局统一管理,如 Windows 下的 Task Manager
  • 网站计数器

四. 实现单例模式的五种常用方式

1. 使用模块导入

前面我们介绍过模块导入只有首次导入会执行模块文件, 后面的导入都是直接使用第一次导入的结果, 因为在模块第一次导入时会生成一个 .pyc 文件, 后面导入直接加载它, 既然如此, 那么 Python 的模块就是一个天然的单例模式

  • test.py 文件内容
class Singletan:
    NAME = " 北极星 "
    PASSWD = "275242996"
    AGE = 22
    SEX = "man"

info = Singletan()
  • 执行文件 正确 演示
from test import info  # 第一次导入执行 test 文件生成 .pyc 文件
from test import info  # 第二次导入直接加载 .pyc 文件

obj1 = info
obj2 = info

print(obj1 is obj2)  # True
  • 执行文件 错误 演示
from test import Singletan  # 第一次导入执行 test 文件生成 .pyc 文件
from test import Singletan  # 第二次导入直接加载 .pyc 文件

info1 = Singletan()
info2 = Singletan()

print(info1 is info2)  # False

2. 使用类装饰器来实现

def Singletan(cls):
    __instance = None        # 定义一个常量, 用来存对象, 以后每次都返回这个对象
    def wrapper(*args,**kwargs):
        nonlocal __instance  # 声明内层的 "__instance" 是外层函数的变量, 不声明报错
        if not __instance:   # 对象不存在, 将类的实例赋值给 "__instance"
            __instance = cls(*args,**kwargs)  
            # 这里是进行赋值操作, 但如果没有声明成外层函数变量, 机器会识别成你是在定义变量, 在定义之前引用会报错
        return __instance    # 对象已经存在时直接将对象返回
    return wrapper

@Singletan  # Singletan(Info)----> 返回 wrapper = Info
class Info:
    def __init__(self):
        ...

obj1 = Info()
obj2 = Info()

print(obj1 is obj2)  # True

3. 使用类的绑定方法实现

class Singletan:
    __instance = None                    # 设置变量存放对象
    NAME = "shawn"
    PASSWD = "275242996"

    @classmethod
    def get_obj(cls):
        if not Singletan.__instance:     # 判断是否对象存在
            Singletan.__instance = cls() # 不存在实例出一个赋值
        return Singletan.__instance      # 存在直接返回这个对象

obj1 = Singletan.get_obj()
obj2 = Singletan.get_obj()

print(obj1 is obj2)  # True
  • 当使用了类的绑定方法实现单例后, 正常实例出来的对象不是单例
obj3 = Singletan()
obj4 = Singletan()

print(obj3 is obj4)  # False

4. 基于 __new__ 方法实现单例

class Singletan:
    __instance = None                   # 设置变量存放对象
    NAME = "shawn"
    AGE = 22

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:         # 判断是否对象存在
            cls.__instance = super().__new__(cls)  # 不存在就实例出一个并赋值
        return cls.__instance          # 存在直接返回这个对象

obj1 = Singletan()
obj2 = Singletan()

print(obj1 is obj2)  # True

5. 通过元类实现

class SingletanType(type):
    __instance = None      # 设置变量存放对象,Singletan 中找不到会跑这来找      

    def __call__(self, *args, **kwargs):
        if not self.__instance:    # 判断是否对象存在
            self.__instance = self.__new__(self)  # 不存在就实例出一个并赋值
            # SingletanType.__instance = self.__new__(self) 直接设置元类的属性, 类中找不到会到元类找(了解)
        return self.__instance     # 存在直接返回这个对象

class Singletan(metaclass=SingletanType):
    NAME = "shawn"
    AGE = 22

obj1 = Singletan()  # 触发元类的 __call__
obj2 = Singletan()

print(obj1 is obj2)  # True
正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计2440字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)