Python 设计模式之享元模式


享元模式( flyweight pattern)属于结构型设计模式,主要用于解决系统中大量创建同一个类的实例时导致的内存激增的问题,它的解决思路是将类的实例属性拆分成外部属性和内部属性。

  • 外部属性:会被外部修改的属性
  • 内部属性:实例创建之后就不会变更的属性

从一个 MP3 案例谈起

现编案例,如有不恰当可以指正

案例
假设我们现在有一个系统用于管理每个人的 MP3 设备,这些 MP3 有不同的颜色、牌子、型号…还有每个人在设备里的个性化配置、自己导入的音乐。

代码实现
系统的早期实现版本如下:

import tracemalloc
from collections import namedtuple

def parse_comma_data(raw_data:str, char=","):
    lines = raw_data.split("\n")
    return (l.split(char) for l in lines)

class MP3:
    def __init__(self, brand, model, memory_size, color, settings, music_list=None):
        self.brand = brand
        self.model = model
        self.memory_size = memory_size
        self.color = color
        self.settings = settings
        self.music_list = music_list
        self.equipment_parameters = bytes(10000000)
        self.system = bytes(10000000)
        
    def __str__(self):
        return f"{
     
     self.model} {
     
     self.color} {
     
     self.memory_size} with id:{
     
     id(self)}"

    def __repr__(self):
        return self.__str__()
    
    def add_music(self, new_music:str):
        if self.music_list is None:
            self.music_list = []
            
        self.music_list.append(new_music)
    
# 系统需要管理的 MP3 设备
mp3_raw_list = """john,sony,S1001,4G,red
cindy,sony,S1001,4G,red
simon,sony,S1001,4G,red
lucy,sony,S1001,4G,red
babala,sony,S1002,4G,red
tom,sony,S1002,4G,red
dikaer,sony,S1005,16G,red"""

def main():
    mp3_devices = {
   
   }
    for data in parse_comma_data(mp3_raw_list):
        name,brand,model,memory_size,color = data
        mp3_devices[name] = MP3(
            brand, model, memory_size, color, settings=name
        )
    
    return mp3_devices
    
if __name__ == "__main__":
    tracemalloc.start()
    mp3_list = main()
    print("内存占用:", tracemalloc.get_traced_memory()[0])
    tracemalloc.stop()
    for name, mp3 in mp3_list.items():
        print(name, mp3)

上面这段代码将mp3_raw_list 中记录的每个独立的 MP3 导入系统中管理,系统可以根据指定用户调用他们对应的 mp3 对象 (mp3_list[name])。

值得注意的是 MP3 class 中有两个属性比较大,比较占内存空间:

        self.equipment_parameters = bytes(10000000)
        self.system = bytes(10000000)

同时,我们调用了 tracemalloc 库来监测内存的使用情况,并在代码最后打印每个设备的 id, 下面是它的控制台输出

内存占用: 140004589
john S1001 red 4G with id:1658049569424
cindy S1001 red 4G with id:1658049567568
simon S1001 red 4G with id:1658049573200
lucy S1001 red 4G with id:1658049567504
babala S1002 red 4G with id:1658049561936
tom S1002 red 4G with id:1658049561808
dikaer S1005 red 16G with id:1658049562576

从这段输出我们可以看到三个点:

  1. 这些 mp3 设备有一些是同一款产品,比如 john、cindy、simon、lucy 他们四个人都是 sony,S1001,4G,red, 但也注意到他们每个人的设置都不一样,这点可以看 mp3.settings 这个属性都不同就知道
  2. 这些 mp3 对象在系统中都是独立的,这点可以从它们的 id 都不同看出来
  3. 每个 mp3 对象的内存开销大部分消耗在设备基础属性(equipment_parameters)和操作系统(system), 每增加一个在线用户就需要增加 20000000 字节的空间消耗:
     # self.equipment_parameters = bytes(10000000)
     # self.system = bytes(10000000)
    上面有 7 个设备,则消耗:
    10000000 * 2 * 7 = 140000000 
    这个数值接近上面的控制台输出 140004589
    

flyweight 模式解决

flyweight pattern 的组件

首先我们来看下 flyweight 模式有哪些关键组件,然后再按些组件的功能定义来实现代码。

  • Flyweight : 包含多个对象共享的固有状态,同一个 flyweight 对象可在不同的上下文中使用。它存储内在状态,并从上下文中接收外在状态。
  • Context: 保存所有原始对象独有的外在状态。当与一个 flyweight 对象配对时,它代表了原始对象的全部状态。
  • Flyweight Factory: 管理现有的 flyweight 实例池,处理 flyweight 实例的创建和重用。Client 通过与 factory 交互来获取 flyweight 实例,并传递 内部属性 以进行检索或创建。
  • Client: 计算或存储 flyweight 对象的外部属性。 它将 flyweight 视为模板对象,并在运行时通过向其方法中传递上下文数据对其进行配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值