享元模式: 通过为相似对象引入数据共享来最小化内存使用,提升性能,一个享元就是一个包含状态的独立的不可变数据的共享对象 使用场景: 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 StrictRedis
import pysnooper
class Myredis:
_inst = None
def __new__(cls, *args, **kwargs):
# 实例不存在则创建redis链接实例
if not cls._inst:
self = super(Myredis, cls).__new__(cls)
self.__conn_redis__(*args, **kwargs)
cls._inst = self
print("cls._inst === {}".format(cls._inst))
return cls._inst
def __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 是3
self.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] = obj
print("cls.pool==={}".format(cls.pool))
return obj
@classmethod
def __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 是2
cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=2>>>
当前实例化的redis == 2
cls===<class '__main__.MyRedisOfShareMode'>
cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>}
None None
obj===<__main__.MyRedisOfShareMode object at 0x7f13fe07df28>
传入的redis db 是3
cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>
当前实例化的redis == 3
cls===<class '__main__.MyRedisOfShareMode'>
cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>, 3: <__main__.MyRedisOfShareMode object at 0x7f13fe07df28>}
########### None None None
"""
享元模式-装饰器实现
from redis import StrictRedis
from functools import wraps
from logzero import logger
def 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_decorator
class MyRedis(object):
def __init__(self, host, port, redis_db):
self.redis_db = redis_db
logger.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 StrictRedis
from logzero import logger
class ConnRedisMetaClass(type):
def __init__(cls, name, bases, dict):
super(ConnRedisMetaClass, cls).__init__(name, bases, dict)
cls._inst_map = {}
@staticmethod
def _make_arguments_to_key(args, kwds):
"""拼接参数"""
key = args
if kwds:
sorted_items = sorted(kwds.items())
for item in sorted_items:
key += item
return key
def __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
"""