《Python编程实战:运用设计模式、并发和程序库创建高质量程序》—— 2.6 享元模式

简介:

本节书摘来自华章出版社《Python编程实战:运用设计模式、并发和程序库创建高质量程序》一 书中的第2章,第2.6节,作者:(美) Mark Summerfield,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.6 享元模式

如果有许多比较小的对象需要处理,而这些小对象很多又彼此相同,那么就可以使用“享元模式”(Flyweight Pattern)。该模式的实现方式为:只给每种对象创建一个实例,并在有需要时共享此实例。
由于Python使用对象引用,所以很自然地体现出了享元模式的思路。比方说,如果有个字符串列表很长,而且其中许多字符串都一样,那么,使用对象引用(也就是变量)来存储要比直接使用“字面量字符串”(literal string)存储节省许多内存。
screenshot

在上述代码片段中,x元组用8个对象引用来保存3个字符串,而y元组则用8个对象引用保存了8个字符串,因为这种简化的写法实际上与_anonymous_item0 = "red", ..., _anonymous_item7 = "green"; y = (_anonymous_item0, ... _anonymous_item7)等效。
要想在Python语言中利用享元模式,最简单的办法可能就是使用dict了,它可以把每个值都同独特的键关联起来。比方说,在创建大量HTML页面时,我们想根据CSS(Cascading Style Sheets,层叠样式表)来指定“字型”(font),这样就不用每次都创建新字型了,而是可以预先(或在首次使用时)把它们保存在dict里面。等需要使用某个字型时,再将其从dict里取出来。这样就能保证每种字型无论使用多少次,都只会创建一次。
有时我们需要处理大量对象,而其中绝大部分或所有对象都互不相同,并且这些对象未必很小。在这种情况下,有个简单的办法可以降低内存用量,这就是使用__slots__。
screenshot

上面这个简单的Point类可以存放点的三维坐标及颜色。由于用了__slots__,所以Point实例都没有自己的dict(也就是没有self.__dict__)。然而,这样做同时也意味着不能向单个对象中随意添加attribute。(该类代码节选自pointstore1.py。)
在某台电脑中测试时,用上述代码创建含有100万个点的元组(测试程序几乎没有做其他事情)需要2.5秒,并占用183MiB内存。若是不用__slots__,那么执行时间能少零点几秒,但内存占用量却高达312MiB。
默认情况下,Python总是会花更多内存来提升执行速度,但如果有必要的话,通常也可以反过来做,那就是通过降低执行速度来节省内存用量。
screenshot

上面列出了第二版Point类的前几行代码(该类节选自pointstore2.py)。它用DBM(键值对)数据库来保存数据,而数据库本身则存放在磁盘文件中。指向DBM的对象引用保存在静态的(也就是类级别的)Point.__dbm变量里。所有Point实例都使用同一份底层DBM文件。我们首先要打开DBM文件,以便后续使用。shelve模块的默认做法是:如果没发现相关的DBM文件,那就自动创建一份。(稍后我们将演示如何保证DBM文件能正常关闭。)
在存储值时,shelve模块会将其“序列化”(pickle),而在获取值时,则会将其“反序列化”(unpickle)。(由于在反序列化的过程中能够执行任意Python代码,所以Python的序列化格式是不安全的。因此,切勿使用由不可信的数据源所提供的序列化数据,也不要将无访问限制的数据序列化。如果想在这些情况下使用序列化数据,那么可通过“校验和”(checksum)及“加密”(encryption)等自制的安全措施来保证数据安全。)
screenshot

上述方法的代码与pointstore1.py中的完全相同,但这些值都会存储到底层的DBM文件里。
screenshot

上述方法可提供Point实例中x、y、z、color等attribute的“键字符串”(key string)。这个键由实例的十六进制ID(ID是由Python语言内置的id()函数所返回的独特数字)及attribute名构成。比方说,某个Point实例的ID是3?954?827,那么其x值所对应的键字符串就是“3C588B:x”,而y值则可由“3C588B:y”这个键查到,其余attribute亦是如此。
screenshot

上述方法会在访问Point对象的某个attribute时(比如执行x = point.x时)调用。
DBM数据库的键与值必须是bytes。好在Python的DBM模块可以接受str或bytes作键,遇到str时,会用默认的UTF-8编码将其转换为bytes。如果像本例一样使用shelve模块,那么凡是可以序列化的值就都能保存到数据库里,因为shelve模块会根据情况在其他类型与bytes之间相互转换。
上面两个方法使我们能够获得与attribute相关的键,并根据键来查出attribute值。另外,由于使用了shelve模块,所以获取到的值会从序列化的bytes自动转换成原本的类型(比方说,获取到的点颜色值会是int或None类型)。
screenshot

设置Point的attribute时(例如执行point.y = y时)会调用上述方法。在该方法中,我们会根据键查出相关的attribute值,并通过shelve模块把值序列化为bytes。
screenshot

在Point类最后,我们通过atexit模块的register()函数来注册DBM的close()方法,使得程序在终止时能够调用此方法。
在某台测试机上创建含有100万个点的数据库大约需要一分钟时间,但程序只占用29MiB内存(外加361MiB的磁盘文件),而第一版程序则要占用183MiB内存。尽管生成DBM文件确实需要一些时间,但只要生成好了,查询速度就会很快,因为大部分操作系统都会把频繁使用的磁盘文件缓存起来。

相关文章
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
190 92
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
103 1
|
1月前
|
[oeasy]python074_ai辅助编程_水果程序_fruits_apple_banana_加法_python之禅
本文回顾了从模块导入变量和函数的方法,并通过一个求和程序实例,讲解了Python中输入处理、类型转换及异常处理的应用。重点分析了“明了胜于晦涩”(Explicit is better than implicit)的Python之禅理念,强调代码应清晰明确。最后总结了加法运算程序的实现过程,并预告后续内容将深入探讨变量类型的隐式与显式问题。附有相关资源链接供进一步学习。
40 4
Python 高级编程与实战:深入理解设计模式与软件架构
本文深入探讨了Python中的设计模式与软件架构,涵盖单例、工厂、观察者模式及MVC、微服务架构,并通过实战项目如插件系统和Web应用帮助读者掌握这些技术。文章提供了代码示例,便于理解和实践。最后推荐了进一步学习的资源,助力提升Python编程技能。
在Python程序中实现LevelDB的海量key的分批次扫描
通过本文的步骤,您可以在Python程序中实现对LevelDB海量key的分批次扫描。这样不仅能够有效地管理大规模数据,还可以避免一次性加载过多数据到内存中,提高程序的性能和稳定性。希望这篇指南能为您的开发工作提供实用的帮助。
104 28
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
Python程序的安全逆向(关于我的OPENAI的APIkey是如何被盗的)
本文介绍了如何使用C语言编写一个简单的文件加解密程序,并讨论了如何为编译后的软件添加图标。此外,文章还探讨了Python的.pyc、.pyd等文件的原理,以及如何生成和使用.pyd文件来增强代码的安全性。通过视频和教程,作者详细讲解了生成.pyd文件的过程,并分享了逆向分析.pyd文件的方法。最后,文章提到可以通过定制Python解释器来进一步保护源代码。
123 6
Python编程入门:打造你的第一个程序
迈出编程的第一步,就像在未知的海洋中航行。本文是你启航的指南针,带你了解Python这门语言的魅力所在,并手把手教你构建第一个属于自己的程序。从安装环境到编写代码,我们将一步步走过这段旅程。准备好了吗?让我们开始吧!
Python编程中的设计模式应用与实践感悟####
本文作为一篇技术性文章,旨在深入探讨Python编程中设计模式的应用价值与实践心得。在快速迭代的软件开发领域,设计模式如同导航灯塔,指引开发者构建高效、可维护的软件架构。本文将通过具体案例,展现设计模式如何在实际项目中解决复杂问题,提升代码质量,并分享个人在实践过程中的体会与感悟。 ####
|
5月前
|
使用Python实现自动化邮件通知:当长时程序运行结束时
本文介绍了如何使用Python实现自动化邮件通知功能,当长时间运行的程序完成后自动发送邮件通知。主要内容包括:项目背景、设置SMTP服务、编写邮件发送函数、连接SMTP服务器、发送邮件及异常处理等步骤。通过这些步骤,可以有效提高工作效率,避免长时间等待程序结果。
202 9

热门文章

最新文章

下一篇
oss创建bucket
目录
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等