python中优雅的使用单例模式
习惯用面相对象的方式来 coding 的朋友,写 python 的时候一定也是喜欢先通过 class 来建模,然后再通过 method 来实现各种逻辑。用这种方式来 coding 一定会频繁的创建类实例,频繁的重复创建对象开销比较大,单例模式可以降低这部分开销。
使用单例模式
简单的使用
最简单的使用单例方式实际上就是在包内就创建好实例,然后外部的包直接使用这个创建好的实例
栗子🌰:
假设 mapper 包的 lookup_mapper.py 是用来查询 lookup 表的一个类,其他模块要通过这个类来查询数据
class LookupMapper(BaseMapper): def find(self, lookup_type: str, code: str) -> Lookup | None: c = self.conn.cursor() try: result = c.execute( 'select id,type,code,value,status from lookup where type = ? and code = ?', (lookup_type, code)) row = result.fetchone() if row is None: return None return Lookup(id=row[0], type=row[1], code=row[2], value=row[3], status=row[4]) finally: c.close() LookupMapperInstance = LookupMapper()在其他包里直接引用LookupMapperInstance
from mapper.lookup_mapper import LookupMapperInstanceinstance1 = LookupMapperInstanceinstance2 = LookupMapperInstanceprint(instance1 == instance2)这种方式使用起来非常简单,但是需要自己提前创建好实例,这里会有个问题,外部的包可能绕过这个实例,直接调用 LookupMapper 的构造函数自己创建实例,这样就导致类实例的使用不统一,下面讲一个更优雅且统一的方式使用单例对象。
优雅的使用
为了让用户调用 LookupMapper 类的构造函数也能返回单例对象,就可以完美解决上面说的使用方式不统一的问题
先来看代码:
from functools import wrapsdef singleton(orig_cls): orig_new = orig_cls.__new__ instance = None @wraps(orig_cls.__new__) def __new__(cls, *args, **kwargs): nonlocal instance if instance is None: instance = orig_new(cls, *args, **kwargs) return instance orig_cls.__new__ = __new__ return orig_cls这里定义了一个 singleton 函数,函数的参数为一个类,singleton 函数做的事情就是定义一个新的__new__函数来覆盖类原始的构造函数。
@wraps 的用法可能新手读者不是很了解,它的作用是让被修饰的对象保持原有的信息,例如类名和文档字符串,这里的作用就是保持orig_cls.__new__函数信息,从代码上来看其实核心思路就是让orig_cls.__new__反过来再修饰singleton函数内部的__new__函数
那怎么让 singleton 函数和类结合起来呢,这里可以使用装饰器模式来增强类的原始功能
@singletonclass LookupMapper(BaseMapper): def find(self, lookup_type: str, code: str) -> Lookup | None: c = self.conn.cursor() try: result = c.execute( 'select id,type,code,value,status from lookup where type = ? and code = ?', (lookup_type, code)) row = result.fetchone() if row is None: return None return Lookup(id=row[0], type=row[1], code=row[2], value=row[3], status=row[4]) finally: c.close() mapper1 = LookupMapper()mapper2 = LookupMapper()print(mapper1 == mapper2)print(mapper1.__new__)上面的代码通过@singleton修饰了原始的 LookupMapper 类。
