《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文件确实需要一些时间,但只要生成好了,查询速度就会很快,因为大部分操作系统都会把频繁使用的磁盘文件缓存起来。

相关文章
|
1月前
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
167 92
|
4月前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
2月前
|
存储 NoSQL 数据库连接
在Python程序中实现LevelDB的海量key的分批次扫描
通过本文的步骤,您可以在Python程序中实现对LevelDB海量key的分批次扫描。这样不仅能够有效地管理大规模数据,还可以避免一次性加载过多数据到内存中,提高程序的性能和稳定性。希望这篇指南能为您的开发工作提供实用的帮助。
82 28
|
4月前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
3月前
|
安全 API C语言
Python程序的安全逆向(关于我的OPENAI的APIkey是如何被盗的)
本文介绍了如何使用C语言编写一个简单的文件加解密程序,并讨论了如何为编译后的软件添加图标。此外,文章还探讨了Python的.pyc、.pyd等文件的原理,以及如何生成和使用.pyd文件来增强代码的安全性。通过视频和教程,作者详细讲解了生成.pyd文件的过程,并分享了逆向分析.pyd文件的方法。最后,文章提到可以通过定制Python解释器来进一步保护源代码。
96 6
|
3月前
|
IDE 程序员 开发工具
Python编程入门:打造你的第一个程序
迈出编程的第一步,就像在未知的海洋中航行。本文是你启航的指南针,带你了解Python这门语言的魅力所在,并手把手教你构建第一个属于自己的程序。从安装环境到编写代码,我们将一步步走过这段旅程。准备好了吗?让我们开始吧!
|
4月前
|
开发者 Python
使用Python实现自动化邮件通知:当长时程序运行结束时
本文介绍了如何使用Python实现自动化邮件通知功能,当长时间运行的程序完成后自动发送邮件通知。主要内容包括:项目背景、设置SMTP服务、编写邮件发送函数、连接SMTP服务器、发送邮件及异常处理等步骤。通过这些步骤,可以有效提高工作效率,避免长时间等待程序结果。
135 9
|
3月前
|
Shell 开发工具 Python
如何在vim里直接运行python程序
如何在vim里直接运行python程序
|
4月前
|
存储 人工智能 数据挖掘
Python编程入门:打造你的第一个程序
本文旨在为初学者提供Python编程的初步指导,通过介绍Python语言的基础概念、开发环境的搭建以及一个简单的代码示例,帮助读者快速入门。文章将引导你理解编程思维,学会如何编写、运行和调试Python代码,从而开启编程之旅。
93 2
|
4月前
|
Python
在Python中,`try...except`语句用于捕获和处理程序运行时的异常
在Python中,`try...except`语句用于捕获和处理程序运行时的异常
100 5