享元模式: 通过为相似对象引入数据共享来最小化内存使用,提升性能,一个享元就是一个包含状态的独立的不可变数据的共享对象 使用场景: 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

享元模式

  1. # 单例模式,错误示范
  2. from redis import StrictRedis
  3. import pysnooper
  4. class Myredis:
  5. _inst = None
  6. def __new__(cls, *args, **kwargs):
  7. # 实例不存在则创建redis链接实例
  8. if not cls._inst:
  9. self = super(Myredis, cls).__new__(cls)
  10. self.__conn_redis__(*args, **kwargs)
  11. cls._inst = self
  12. print("cls._inst === {}".format(cls._inst))
  13. return cls._inst
  14. def __conn_redis__(self, redis_db):
  15. print("传入的redis db 是{}".format(redis_db))
  16. self.r = StrictRedis(host='127.0.0.1', port=6379, db=redis_db)
  17. print("self.r === {}".format(self.r))
  18. def set_data(self, key, value):
  19. self.r.set(key, value)
  20. if __name__ == "__main__":
  21. a = Myredis(3)
  22. b = Myredis(4)
  23. print(a, b)
  24. # 这里会发现,即使链接的是不用的redis 数据库,使用的都是同一个实例,DB4永远不会都数据
  25. """
  26. 传入的redis db 是3
  27. self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>
  28. cls._inst === <__main__.Myredis object at 0x7f6b064e7d68>
  29. cls._inst === <__main__.Myredis object at 0x7f6b064e7d68>
  30. <__main__.Myredis object at 0x7f6b064e7d68> <__main__.Myredis object at 0x7f6b064e7d68>
  31. """
  1. # 享元模式
  2. class MyRedisOfShareMode:
  3. pool = dict() # 缓存池
  4. def __new__(cls, redis_host, redis_port, redis_db):
  5. # 将ip、post、db拼接组合为实例唯一标识
  6. cache_key = '-'.join([redis_host, str(redis_port), str(redis_db)])
  7. obj = cls.pool.get(cache_key, None)
  8. # 如果唯一标识不存在,则重新创建redis链接实例
  9. if not obj:
  10. obj = object.__new__(cls)
  11. print("obj==={}".format(obj))
  12. cls.__conn_redis__(redis_host, redis_port, redis_db)
  13. print("当前实例化的redis == {}".format(cache_key))
  14. print("cls==={}".format(cls))
  15. # 将已创建的实例保存到缓存池中
  16. cls.pool[cache_key] = obj
  17. print("cls.pool==={}".format(cls.pool))
  18. return obj
  19. @classmethod
  20. def __conn_redis__(cls, redis_host, redis_port, redis_db):
  21. print("传入的redis db 是{}".format(redis_db))
  22. cls.r = StrictRedis(host=redis_host, port=redis_port, db=redis_db)
  23. print("cls.r === {}".format(cls.r))
  24. def set_data(self, name, value):
  25. self.r.set(name, value)
  26. if __name__ == "__main__":
  27. a = MyRedisOfShareMode('127.0.0.1', 6379, 2).set_data('aaa', '111')
  28. b = MyRedisOfShareMode('127.0.0.1', 6379, 2).set_data('bbb', '222')
  29. print(a, b)
  30. c = MyRedisOfShareMode('127.0.0.1', 6379, 3).set_data('ccc', '11221')
  31. print("###########", a, b, c)
  32. # 可以看出,当ip、port、db一致时,是不会重复创建redis链接实例的
  33. """
  34. obj===<__main__.MyRedisOfShareMode object at 0x7f13feb62e48>
  35. 传入的redis db 是2
  36. cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=2>>>
  37. 当前实例化的redis == 2
  38. cls===<class '__main__.MyRedisOfShareMode'>
  39. cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>}
  40. None None
  41. obj===<__main__.MyRedisOfShareMode object at 0x7f13fe07df28>
  42. 传入的redis db 是3
  43. cls.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>
  44. 当前实例化的redis == 3
  45. cls===<class '__main__.MyRedisOfShareMode'>
  46. cls.pool==={2: <__main__.MyRedisOfShareMode object at 0x7f13feb62e48>, 3: <__main__.MyRedisOfShareMode object at 0x7f13fe07df28>}
  47. ########### None None None
  48. """

享元模式-装饰器实现

  1. from redis import StrictRedis
  2. from functools import wraps
  3. from logzero import logger
  4. def redis_conn_decorator(cls):
  5. _instance = {} # 缓存池
  6. @wraps(cls)
  7. def wrappers(*args, **kwargs):
  8. # 将host、port、db 拼接作为唯一标识
  9. cache_key = '-'.join([*args])
  10. # 唯一标识不存在则创建redis 链接实例
  11. if cache_key not in _instance:
  12. logger.info("新创建了实例, cache_key == {}".format(cache_key))
  13. logger.info("cls===={}".format(cls))
  14. # 将创建的实例保存到缓存池
  15. _instance[cache_key] = cls(*args)
  16. logger.info("当前_instance == {}".format(_instance))
  17. return _instance[cache_key]
  18. return wrappers
  19. @redis_conn_decorator
  20. class MyRedis(object):
  21. def __init__(self, host, port, redis_db):
  22. self.redis_db = redis_db
  23. logger.info("传入的redis db 是{}".format(self.redis_db))
  24. self.r = StrictRedis(host=host, port=int(port), db=int(self.redis_db))
  25. logger.info("self.r === {}".format(self.r))
  26. def set_data(self, name, value):
  27. logger.info(f"name={name}, value={value}")
  28. self.r.set(name, value)
  29. if __name__ == "__main__":
  30. a = MyRedis('127.0.0.1', "6379", "3")
  31. b = MyRedis('127.0.0.1', "6379", "3")
  32. c = MyRedis('127.0.0.1', "6379", "4")
  33. print(a, b, c)
  34. a.set_data("a_decorator", 'aa')
  35. b.set_data("b_decorator", 'bb')
  36. c.set_data("c_decorator", 'cc')
  37. """
  38. [I 220717 15:26:51 share_meta_with_decorator:26] 新创建了实例, cache_key == 127.0.0.1-6379-3
  39. [I 220717 15:26:51 share_meta_with_decorator:27] cls====<class '__main__.MyRedis'>
  40. [I 220717 15:26:51 share_meta_with_decorator:45] 传入的redis db 是3
  41. [I 220717 15:26:51 share_meta_with_decorator:48] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=3>>>
  42. [I 220717 15:26:51 share_meta_with_decorator:32] 当前_instance == {'127.0.0.1-6379-3': <__main__.MyRedis object at 0x7f9bab805550>}
  43. [I 220717 15:26:51 share_meta_with_decorator:32] 当前_instance == {'127.0.0.1-6379-3': <__main__.MyRedis object at 0x7f9bab805550>}
  44. [I 220717 15:26:51 share_meta_with_decorator:26] 新创建了实例, cache_key == 127.0.0.1-6379-4
  45. [I 220717 15:26:51 share_meta_with_decorator:27] cls====<class '__main__.MyRedis'>
  46. [I 220717 15:26:51 share_meta_with_decorator:45] 传入的redis db 是4
  47. [I 220717 15:26:51 share_meta_with_decorator:48] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=4>>>
  48. [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>}
  49. <__main__.MyRedis object at 0x7f9bab805550> <__main__.MyRedis object at 0x7f9bab805550> <__main__.MyRedis object at 0x7f9ba85886a0>
  50. [I 220717 15:26:51 share_meta_with_decorator:53] name=a_decorator, value=aa
  51. [I 220717 15:26:51 share_meta_with_decorator:53] name=b_decorator, value=bb
  52. [I 220717 15:26:51 share_meta_with_decorator:53] name=c_decorator, value=cc
  53. """

享元模式-元类实现

  1. from redis import StrictRedis
  2. from logzero import logger
  3. class ConnRedisMetaClass(type):
  4. def __init__(cls, name, bases, dict):
  5. super(ConnRedisMetaClass, cls).__init__(name, bases, dict)
  6. cls._inst_map = {}
  7. @staticmethod
  8. def _make_arguments_to_key(args, kwds):
  9. """拼接参数"""
  10. key = args
  11. if kwds:
  12. sorted_items = sorted(kwds.items())
  13. for item in sorted_items:
  14. key += item
  15. return key
  16. def __call__(cls, *args, **kwargs):
  17. # 将host、port、db 拼接作为唯一标识
  18. cache_key = f'{cls}_{cls._make_arguments_to_key(args, kwargs)}'
  19. logger.info(f"cache_key === {cache_key}")
  20. # 唯一标识不存在则创建redis 链接实例
  21. if cache_key not in cls._inst_map:
  22. cls._inst_map[cache_key] = super().__call__(*args, **kwargs)
  23. logger.info(f"cls._inst_map === {cls._inst_map}")
  24. return cls._inst_map[cache_key]
  25. class Myredis(metaclass=ConnRedisMetaClass):
  26. def __init__(self, host, port, redis_db):
  27. logger.info(f"传入的host = {host}, port = {port}, redis_db = {redis_db}")
  28. self.r = StrictRedis(host=host, port=port, db=redis_db)
  29. logger.info("self.r === {}".format(self.r))
  30. def set_data(self, name, value):
  31. logger.info(f"name={name}, value={value}")
  32. self.r.set(name, value)
  33. if __name__ == "__main__":
  34. a = Myredis('127.0.0.1', 6379, 2)
  35. b = Myredis('127.0.0.1', 6379, 2)
  36. c = Myredis('127.0.0.1', 6379, 4)
  37. logger.info(f"{a}, {b}, {c}")
  38. a.set_data("a", 'metaclass-aa')
  39. b.set_data("b", 'metaclass-bb')
  40. c.set_data("c", 'metaclass-cc')
  41. """
  42. [I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 2)
  43. [I 220717 15:28:42 share_meta_with_metaclass:47] 传入的host = 127.0.0.1, port = 6379, redis_db = 2
  44. [I 220717 15:28:42 share_meta_with_metaclass:50] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=2>>>
  45. [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>}
  46. [I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 2)
  47. [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>}
  48. [I 220717 15:28:42 share_meta_with_metaclass:34] cache_key === <class '__main__.Myredis'>_('127.0.0.1', 6379, 4)
  49. [I 220717 15:28:42 share_meta_with_metaclass:47] 传入的host = 127.0.0.1, port = 6379, redis_db = 4
  50. [I 220717 15:28:42 share_meta_with_metaclass:50] self.r === Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=4>>>
  51. [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>}
  52. [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>
  53. [I 220717 15:28:42 share_meta_with_metaclass:55] name=a, value=metaclass-aa
  54. [I 220717 15:28:42 share_meta_with_metaclass:55] name=b, value=metaclass-bb
  55. [I 220717 15:28:42 share_meta_with_metaclass:55] name=c, value=metaclass-cc
  56. """