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

相关文章
|
26天前
|
数据处理 UED Python
Python 进度条:告别枯燥等待,让你的程序动感十足!
Python 进度条:告别枯燥等待,让你的程序动感十足!
51 1
|
4天前
|
存储 Shell 区块链
怎么把Python脚本打包成可执行程序?
该文档介绍了如何将Python脚本及其运行环境打包成EXE可执行文件,以便在不具备Python环境的计算机上运行。首先确保Python脚本能够正常运行,然后通过安装PyInstaller并使用`--onefile`参数将脚本打包成独立的EXE文件。此外,还提供了去除命令行窗口和指定可执行文件图标的详细方法。这些步骤帮助用户轻松地将Python程序分发给最终用户。
怎么把Python脚本打包成可执行程序?
|
20天前
|
设计模式 存储 Java
【十】设计模式~~~结构型模式~~~享元模式(Java)
文章详细介绍了享元模式(Flyweight Pattern),这是一种对象结构型模式,通过共享技术实现大量细粒度对象的重用,区分内部状态和外部状态来减少内存中对象的数量,提高系统性能。通过围棋棋子的设计案例,展示了享元模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了单纯享元模式和复合享元模式以及与其他模式的联用。
【十】设计模式~~~结构型模式~~~享元模式(Java)
|
3天前
|
算法 程序员 Linux
Python编程入门:构建你的第一个程序
【9月更文挑战第4天】编程是现代技术发展的基石,而Python作为一门简洁、易学且功能强大的编程语言,已成为众多初学者的首选。本文将引导你通过一个简单的Python程序,探索编程世界的奥秘,并了解如何利用Python实现基本的算法逻辑。无论你是完全的新手还是希望巩固基础的开发者,这篇文章都将为你提供一个清晰的学习路径。从安装Python环境开始,到编写第一个程序,我们将一步步揭开编程的神秘面纱。
|
8天前
|
Python
Python编程入门:构建你的第一个程序
【8月更文挑战第31天】本文是面向初学者的Python编程指南,将引导读者通过实际案例学习Python基础。我们将一起编写一个简单的程序来展示如何运用变量、控制结构和函数等概念。文章末尾附有完整代码示例供参考和实践。
|
8天前
|
小程序 Python
Python 编程入门:打造你的第一个程序
【8月更文挑战第31天】 在数字化时代,编程已成为一项宝贵的技能。本文将通过一个简单示例引导初学者步入Python编程的世界。我们将从基础语法开始,逐步构建一个小程序,并在此过程中探索编程的逻辑思维与问题解决策略。无论你是科技爱好者还是职场新人,这篇文章都将为你开启编程之旅提供助力。
|
8天前
|
程序员 Python
Python 编程入门:打造你的第一个程序
【8月更文挑战第31天】编程初学者常常在起步时感到迷茫。本文将通过浅显易懂的方式,带领读者从零开始,一步步构建他们的第一个 Python 程序。我们将探索 Python 的安装、基础语法,并通过一个实际的项目——简易计算器,来巩固学习成果。无论你的背景如何,这篇文章都将为你打开编程世界的大门,让你轻松上手,享受编程的乐趣。
|
8天前
|
IDE 程序员 开发工具
Python编程入门:构建你的第一个程序
【8月更文挑战第31天】 本文是专为编程新手设计的Python入门指南,旨在通过一个简单实例引导读者了解Python编程的基础。文章将逐步展示如何从零开始编写一个“Hello, World!”程序,并解释代码中每一部分的功能,帮助初学者建立对Python语言的初步认识和理解。通过本文,读者将学会设置编程环境、编写简单的Python代码以及运行和调试程序。
|
8天前
|
存储 自然语言处理 小程序
Python编程入门:从零基础到编写你的第一个程序
【8月更文挑战第31天】探索编码的奥秘,开启你的编程之旅。本文将引导你通过Python语言轻松步入编程世界。无需担心没有基础,我们将从最基础的概念开始,一步步带你理解编程的核心思想,并最终实现一个属于你自己的小程序。让我们一同见证,如何将一行行代码转化为能够执行任务的数字魔法。
|
10天前
|
PHP C++ Python
右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)
右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)
12 0
下一篇
DDNS