享元模式: 通过为相似对象引入数据共享来最小化内存使用,提升性能,一个享元就是一个包含状态的独立的不可变数据的共享对象 使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景 例如建立一个数据库链接,不希望建立多个链接,但又期望在同一个解释器下操作多台机器的数据库,但机器的IP端口不同的时候,就需要创建新链接,否则则直接使用已创建的链接 主要解决: 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
参考文章: https://www.runoob.com/design-pattern/flyweight-pattern.html https://github.com/ydf0509/python36patterns/blob/master/%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F-%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F-%E5%85%83%E7%B1%BB.py
享元模式
# 单例模式,错误示范from redis import StrictRedisimport pysnooperclass Myredis:_inst = Nonedef __new__(cls, *args, **kwargs):# 实例不存在则创建redis链接实例if not cls._inst:self = super(Myredis, cls).__new__(cls)self.__conn_redis__(*args, **kwargs)cls._inst = selfprint("cls._inst === {}".format(cls._inst))return cls._instdef __conn_redis__(self, redis_db):print("传入的redis db 是{}".format(redis_db))self.r = StrictRedis(host='127.0.0.1', port=6379, db=redis_db)print("self.r === {}".format(self.r))def set_data(self, key, value):self.r.set(key, value)if __name__ == "__main__":a = Myredis(3)b = Myredis(4)print(a, b)# 这里会发现,即使链接的是不用的redis 数据库,使用的都是同一个实例,DB4永远不会都数据"""传入的redis db 是3self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>cls._inst === <__main__.Myredis object at 0x7f6b064e7d68>cls._inst === <__main__.Myredis object at 0x7f6b064e7d68><__main__.Myredis object at 0x7f6b064e7d68> <__main__.Myredis object at 0x7f6b064e7d68>"""
# 享元模式class MyRedisOfShareMode:pool = dict() # 缓存池def __new__(cls, redis_host, redis_port, redis_db):# 将ip、post、db拼接组合为实例唯一标识cache_key = '-'.join([redis_host, str(redis_port), str(redis_db)])obj = cls.pool.get(cache_key, None)# 如果唯一标识不存在,则重新创建redis链接实例if not obj:obj = object.__new__(cls)print("obj==={}".format(obj))cls.__conn_redis__(redis_host, redis_port, redis_db)print("当前实例化的redis == {}".format(cache_key))print("cls==={}".format(cls))# 将已创建的实例保存到缓存池中cls.pool[cache_key] = objprint("cls.pool==={}".format(cls.pool))return obj@classmethoddef __conn_redis__(cls, redis_host, redis_port, redis_db):print("传入的redis db 是{}".format(redis_db))cls.r = StrictRedis(host=redis_host, port=redis_port, db=redis_db)print("cls.r === {}".format(cls.r))def set_data(self, name, value):self.r.set(name, value)if __name__ == "__main__":a = MyRedisOfShareMode('127.0.0.1', 6379, 2).set_data('aaa', '111')b = MyRedisOfShareMode('127.0.0.1', 6379, 2).set_data('bbb', '222')print(a, b)c = MyRedisOfShareMode('127.0.0.1', 6379, 3).set_data('ccc', '11221')print("###########", a, b, c)# 可以看出,当ip、port、db一致时,是不会重复创建redis链接实例的"""obj===<__main__.MyRedisOfShareMode object at 0x7f13feb62e48>传入的redis db 是2cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=2>>>当前实例化的redis == 2cls===<class '__main__.MyRedisOfShareMode'>cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>}None Noneobj===<__main__.MyRedisOfShareMode object at 0x7f13fe07df28>传入的redis db 是3cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>当前实例化的redis == 3cls===<class '__main__.MyRedisOfShareMode'>cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>, 3: <__main__.MyRedisOfShareMode object at 0x7f13fe07df28>}########### None None None"""
享元模式-装饰器实现
from redis import StrictRedisfrom functools import wrapsfrom logzero import loggerdef redis_conn_decorator(cls):_instance = {} # 缓存池@wraps(cls)def wrappers(*args, **kwargs):# 将host、port、db 拼接作为唯一标识cache_key = '-'.join([*args])# 唯一标识不存在则创建redis 链接实例if cache_key not in _instance:logger.info("新创建了实例, cache_key == {}".format(cache_key))logger.info("cls===={}".format(cls))# 将创建的实例保存到缓存池_instance[cache_key] = cls(*args)logger.info("当前_instance == {}".format(_instance))return _instance[cache_key]return wrappers@redis_conn_decoratorclass MyRedis(object):def __init__(self, host, port, redis_db):self.redis_db = redis_dblogger.info("传入的redis db 是{}".format(self.redis_db))self.r = StrictRedis(host=host, port=int(port), db=int(self.redis_db))logger.info("self.r === {}".format(self.r))def set_data(self, name, value):logger.info(f"name={name}, value={value}")self.r.set(name, value)if __name__ == "__main__":a = MyRedis('127.0.0.1', "6379", "3")b = MyRedis('127.0.0.1', "6379", "3")c = MyRedis('127.0.0.1', "6379", "4")print(a, b, c)a.set_data("a_decorator", 'aa')b.set_data("b_decorator", 'bb')c.set_data("c_decorator", 'cc')"""[I 220717 15:26:51 share_meta_with_decorator:26] 新创建了实例, cache_key == 127.0.0.1-6379-3[I 220717 15:26:51 share_meta_with_decorator:27] cls====<class '__main__.MyRedis'>[I 220717 15:26:51 share_meta_with_decorator:45] 传入的redis db 是3[I 220717 15:26:51 share_meta_with_decorator:48] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>[I 220717 15:26:51 share_meta_with_decorator:32] 当前_instance == {'127.0.0.1-6379-3': <__main__.MyRedis object at 0x7f9bab805550>}[I 220717 15:26:51 share_meta_with_decorator:32] 当前_instance == {'127.0.0.1-6379-3': <__main__.MyRedis object at 0x7f9bab805550>}[I 220717 15:26:51 share_meta_with_decorator:26] 新创建了实例, cache_key == 127.0.0.1-6379-4[I 220717 15:26:51 share_meta_with_decorator:27] cls====<class '__main__.MyRedis'>[I 220717 15:26:51 share_meta_with_decorator:45] 传入的redis db 是4[I 220717 15:26:51 share_meta_with_decorator:48] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=4>>>[I 220717 15:26:51 share_meta_with_decorator:32] 当前_instance == {'127.0.0.1-6379-3': <__main__.MyRedis object at 0x7f9bab805550>, '127.0.0.1-6379-4': <__main__.MyRedis object at 0x7f9ba85886a0>}<__main__.MyRedis object at 0x7f9bab805550> <__main__.MyRedis object at 0x7f9bab805550> <__main__.MyRedis object at 0x7f9ba85886a0>[I 220717 15:26:51 share_meta_with_decorator:53] name=a_decorator, value=aa[I 220717 15:26:51 share_meta_with_decorator:53] name=b_decorator, value=bb[I 220717 15:26:51 share_meta_with_decorator:53] name=c_decorator, value=cc"""
享元模式-元类实现
from redis import StrictRedisfrom logzero import loggerclass ConnRedisMetaClass(type):def __init__(cls, name, bases, dict):super(ConnRedisMetaClass, cls).__init__(name, bases, dict)cls._inst_map = {}@staticmethoddef _make_arguments_to_key(args, kwds):"""拼接参数"""key = argsif kwds:sorted_items = sorted(kwds.items())for item in sorted_items:key += itemreturn keydef __call__(cls, *args, **kwargs):# 将host、port、db 拼接作为唯一标识cache_key = f'{cls}_{cls._make_arguments_to_key(args, kwargs)}'logger.info(f"cache_key === {cache_key}")# 唯一标识不存在则创建redis 链接实例if cache_key not in cls._inst_map:cls._inst_map[cache_key] = super().__call__(*args, **kwargs)logger.info(f"cls._inst_map === {cls._inst_map}")return cls._inst_map[cache_key]class Myredis(metaclass=ConnRedisMetaClass):def __init__(self, host, port, redis_db):logger.info(f"传入的host = {host}, port = {port}, redis_db = {redis_db}")self.r = StrictRedis(host=host, port=port, db=redis_db)logger.info("self.r === {}".format(self.r))def set_data(self, name, value):logger.info(f"name={name}, value={value}")self.r.set(name, value)if __name__ == "__main__":a = Myredis('127.0.0.1', 6379, 2)b = Myredis('127.0.0.1', 6379, 2)c = Myredis('127.0.0.1', 6379, 4)logger.info(f"{a}, {b}, {c}")a.set_data("a", 'metaclass-aa')b.set_data("b", 'metaclass-bb')c.set_data("c", 'metaclass-cc')"""[I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 2)[I 220717 15:28:42 share_meta_with_metaclass:47] 传入的host = 127.0.0.1, port = 6379, redis_db = 2[I 220717 15:28:42 share_meta_with_metaclass:50] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=2>>>[I 220717 15:28:42 share_meta_with_metaclass:40] cls._inst_map === {"<class '__main__.Myredis'>_('127.0.0.1', 6379, 2)": <__main__.Myredis object at 0x7fb918eae908>}[I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 2)[I 220717 15:28:42 share_meta_with_metaclass:40] cls._inst_map === {"<class '__main__.Myredis'>_('127.0.0.1', 6379, 2)": <__main__.Myredis object at 0x7fb918eae908>}[I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 4)[I 220717 15:28:42 share_meta_with_metaclass:47] 传入的host = 127.0.0.1, port = 6379, redis_db = 4[I 220717 15:28:42 share_meta_with_metaclass:50] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=4>>>[I 220717 15:28:42 share_meta_with_metaclass:40] cls._inst_map === {"<class '__main__.Myredis'>_('127.0.0.1', 6379, 2)": <__main__.Myredis object at 0x7fb918eae908>, "<class '__main__.Myredis'>_('127.0.0.1', 6379, 4)": <__main__.Myredis object at 0x7fb91578cbe0>}[I 220717 15:28:42 share_meta_with_metaclass:64] <__main__.Myredis object at 0x7fb918eae908>, <__main__.Myredis object at 0x7fb918eae908>, <__main__.Myredis object at 0x7fb91578cbe0>[I 220717 15:28:42 share_meta_with_metaclass:55] name=a, value=metaclass-aa[I 220717 15:28:42 share_meta_with_metaclass:55] name=b, value=metaclass-bb[I 220717 15:28:42 share_meta_with_metaclass:55] name=c, value=metaclass-cc"""
