设计模式之单例模式、类属性、装饰器、元类(metaclass)、基于__new__方法

【1】设计模式之单例模式

  • 经典设计模式总共有23种,但其中只有少数几种被广泛使用。
  • 常见的设计模式有单例模式、工厂模式、观察者模式、适配器模式、策略模式、装饰器模式、代理模式等。
  • 实际常用的可能不超过其中的一半
  • 其中单例模式是主要使用的设计模式之一

【2】为什么要单例模式

  • 单例设计模式(Singleton Design Pattern): 一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
  • 当一个类的功能比较单一,只需要一个实例对象就可以完成需求时,就可以使用单例模式来节省内存资源。

【通常】单例模式创建的对象是进程唯一的, 单例类中对象的唯一性的作用范围是进程内的,在进程间是不唯一的。

【3】如何实现一个单例

  • 要实现一个单例
    • 需要考虑对象创建时的线程安全问题
    • 需要考虑是否支持延迟加载
    • 考虑获取实例的性能是否高(是否加锁)
  • 在python中,我们可以使用多种方法来实现单例模式
    • 使用模块
    • 使用装饰器
    • 使用类(方法)
    • 基于__new__方法实现
    • 基于元类metaclass实现

【一】类属性

【1】类产生对象

  • 当有一个类,其中类里面有很多方法
  • 当我们想要使用这些方法的时候,我们的第一想法是实例化得到一个对象,再用对象去调用相应的方法
  • 但是若每一个人都想使用这一个方法的时候,就需要每一个人都去实例化对象,用对象调用方法
# 创建一个普通的类
class MysqlControl(object):
    pass


# 实例化类得到对象 --- 类只要加 () 实例化就会产生一个全新的对象
obj_one = MysqlControl()
obj_two = MysqlControl()

# 查看对象  --- 发现虽然是同一个类实例化得到的对象,但是对象的地址不一样
print(obj_one)
# <__main__.MysqlControl object at 0x00000204C2BB3FD0>
print(obj_two)
# <__main__.MysqlControl object at 0x00000204C2BB3F40>
print(obj_one is obj_two)
# False

【2】类属性包装成方法

class Singleton(object):
    _instance = None

    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = Singleton('127.0.0.1', 3306)
        return cls._instance


obj_one = Singleton.get_instance()
obj_two = Singleton.get_instance()

print(obj_one) 
# <__main__.Singleton object at 0x0000023CA8B03E50>
print(obj_two)
# <__main__.Singleton object at 0x0000023CA8B03E50>
print(obj_one is obj_two)
# True 
# 输出 True,表示是同一个实例
  • 使用类属性保存实例,通过类方法获取实例。
  • 在第一次调用get_instance方法时创建实例,并在后续调用中直接返回该实例。

【二】装饰器

def singleton(cls):
    instances = {}

    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return wrapper


@singleton
class Singleton:
    pass


singleton_one = Singleton()
singleton_two = Singleton()

print(singleton_one)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_two)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_one is singleton_two)
# True

# 输出 True,表示是同一个实例

# # 使用 __call__ 方法获取单例
singleton_three = Singleton()
print(singleton_one is singleton_three)
# True
  • 使用装饰器将原来的类包装成一个新的类,通过闭包和字典保存实例。
  • 在每次实例化时,先检查字典中是否已经存在该类的实例,如果不存在才创建实例并返回。

【三】元类(metaclass)

class SingletonType(type):
    def __init__(cls, name, bases, attrs):
        super(SingletonType, cls).__init__(name, bases, attrs)
        cls.instance = None

    def __call__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super(SingletonType, cls).__call__(*args, **kwargs)
        return cls.instance


class Singleton(metaclass=SingletonType):
    pass


singleton_one = Singleton()
singleton_two = Singleton()

print(singleton_one)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_two)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_one is singleton_two)
# True

# 输出 True,表示是同一个实例

# # 使用 __call__ 方法获取单例
singleton_three = Singleton()
print(singleton_one is singleton_three)
# True
  • 定义一个元类,在元类的__call__方法中判断实例是否已存在,如果不存在则调用父类的__call__方法来创建并返回实例。

【四】基于__new__方法

【1】思路

  • 在Python中,对象的实例化过程通常遵循以下步骤:
    • 首先,执行类的__new__方法,如果未定义此方法,将默认调用父类的__new__方法来创建一个实例化对象。
    • 接着,再执行__init__方法来对这个新创建的对象进行初始化。
  • 我们可以充分利用这个实例化过程来实现单例模式。
    • 具体做法是在类的__new__方法中判断是否已经存在实例,如果存在,则直接返回现有的实例,否则创建一个新的实例。
    • 这样就能够确保只有一个实例存在,从而实现了单例模式的效果。

【2】代码

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(cls)
        return cls._instance


singleton_one = Singleton()
singleton_two = Singleton()

print(singleton_one)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_two)
# <__main__.Singleton object at 0x000001C0B531A770>
print(singleton_one is singleton_two)
# True

# 输出 True,表示是同一个实例

# # 使用 __call__ 方法获取单例
singleton_three = Singleton()
print(singleton_one is singleton_three)
# True
  • 重写__new__方法,在实例化对象时判断类中是否已有实例,如果没有则调用父类的__new__方法来创建并返回。

【3】优势

  • 这种实现方式会导致频繁加锁、释放锁,以及并发度低等问题,频繁的调用会产生性能瓶颈。

【五】基于模块

# singleton.py
class Singleton:
    pass

singleton_instance = Singleton()
  • 将实例化操作放在模块级别,通过导入该模块来获取实例。
  • 由于Python模块在运行时只会被导入一次,因此保证了实例的单一性。
posted @ 2024-05-20 08:45  光头大炮  阅读(8)  评论(0编辑  收藏  举报