热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python性能优化【2】--高效的使用序列与字典、集合

本篇主要对Python常用的几种数据结构:列表、元组、字典、集合以及numpy进行简单的分析,并根据其增删改查的内存分配特点提供相应的使用建议。一.可变数组:列表列表lis

本篇主要对 Python 常用的几种数据结构: 列表、元组、字典、集合以及 numpy 进行简单的分析,并根据其增删改查的内存分配特点提供相应的使用建议。

一. 可变数组: 列表

列表 list 是一个动态的数组,支持 resize,其元素个数是可变的。其可变性的代价在于列表存储需要额外的内存和额外的计算。

列表可变的内存变化

当长度为 N 的 Python 列表第一次需要添加数据时,Python 会创建一个新的列表,足够存放原来的 N 个元素以及需要额外添加的新元素,同时会额外的预留出空间,为将来的新增元素作准备。假设此时的最大容量为 M,后来新加元素时,如果新加后的元素个数不超过 M 个,可以直接保存,如果超过 M 个,则会创建新的列表,将旧列表的元素和新加的元素存储到新列表中,清除旧的列表。新列表也会留出额外的空间。

可以看出,列表的可变性代价就是其需要额外预留的存储空间和计算新列表的长度的运算过程。当需要创建大量的小列表或者存在一个长度很大的大型列表时,其额外的存储空间消耗将会非常严重,需要考虑使用更优的存储方式

二. 静态数组: 元组

元组是固定且不可变的,一旦创建,其内容和大小就不能在被修改。比较适合存储一个不会改变的事物的多个属性。

元组因为其不可变性,因此并不会预留额外的存储空间,因此相对于列表,其资源占用更少。另外对元组并没有提供类似列表 append 的操作,可以通过元组相加来实现元组的变化,不过返回的是一个新的元组。任何对元组的元素就行修改的操作都会导致新的内存分配和复制操作,然后返回一个新的元组

元组其他特性

  • 资源缓存: Python 有自己的垃圾回收机制,然是元组即使不在被使用,也不会被立刻清理将内存还给系统,还是留待未来使用,当需要一个同样大小的内存时,就不用在请求系统进行新的内存资源分配了。这意味着元组的创建非常轻量级。其比列表创建要快得多。示例如下
    这里写图片描述

下面总结下 Python 列表与元组的异同:

  • 列表元素与长度可变,元组不可变,其内部数据一旦创建便无法修改
  • 元组缓存于 Python 的运行环境,因此每次创建元组时无需访问内核去分配内存
  • 列表适合保存多个互相独立对象的数据集合;元素更合适描述一个不会改变的事物的多个属性

下面是 《Python 高性能编程》 中记在的一个例子:

  • 前 20 个质数: 数据是静态且不会改变,使用元组
  • 一个人的年龄,身高,体重: 数据是变化的,使用列表
  • 编程语言的名字: 数据是会不断变化的,使用列i表
  • 一个人的生日和出生地: 出生之后不会变化,使用元组
  • 某次台球的游戏结果: 结束后不会变化,使用元组
  • 一系列台球的游戏结果: 随着游戏次数的变化而变化,使用列表

高效搜索

在很多时候,先排序后搜索是最佳的查找方案。排序后使用二分搜索进行查找,其平均情况的复杂度是 O(logN)。关于二分搜索可以使用 Python 的 bisect 模块,具体使用可以参阅其文档 bisect 模块

三. 字典和集合

字典和集合内存的数据结构是散列表,通过散列表其查询和插入的效率可以达到 O(1)。字典是键值对的组合,而集合就是一堆键的组合

1. 字典的插入与获取原理

新数据的插入主要取决的数据的散列值和其散列值如何跟其他对象进行比较,其插入过程如下:

  1. 插入数据时,首先计算键的散列值并掩码来得到一个有效的数组索引
  2. 通过索引找到对应的桶(即存储空间),如果该空间已经被使用,则需要通过一个简单的线性函数计算出新的索引,这一方法称为嗅探
  3. 根据嗅探后获得的新索引找到对应的存储空间,如果空间未被使用,则将键或者键值插入到这一内存块中。

以上就是字典与集合的插入过程,查询也与此过程类似,首先计算键的散列值得到索引,然后根据索引检索到对应的内存空间,如果该处空间的键与现有键不一致,则通过嗅探继续
计算新的索引进行查找,直到找到数据或者找到一个空桶,空桶意味着数据不存在。

2. 字典与命名空间

【1】 Python 访问一个变量、函数或者模块的顺序
  1. 查找 locals 数组,其内保存了所有本地变量的条目,这是唯一一处不需要字典查询的部分
  2. 如果不在 locals 数组中,则搜索 globals() 字典
  3. 如果依旧不在字典中,则搜索 builtin 对象

当我们需要引入其他模块的函数使用时,更好的方式就是显式的导入函数,而不是只导入模块,这样可以减少一次查询。当需要在循环中使用某些函数时,更好的方式是创建一个本地变量来保存一个
函数的全局引用。

import datetime # 使用 datetime 时会查询两次,一次查询 datetime 模块,另一次查询 datetime 函数
from datetime import datetime # 这里只有一次查询 datetime 函数的操作

# 使用本地变量保存函数的全局引用,可以减少查询,提高性能
from math import sin
def high_loop(n):
local_sin = sin
result = 0
for i in range(n):
result += sin(i)

推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Excel数据处理中的七个查询匹配函数详解
    本文介绍了Excel数据处理中的七个查询匹配函数,以vlookup函数为例进行了详细讲解。通过示例和语法解释,说明了vlookup函数的用法和参数的含义,帮助读者更好地理解和运用查询匹配函数进行数据处理。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
Dear丶尐英
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有