Python Cookbook-5.12 检查序列的成员

news2025/4/18 21:17:45

任务

你需要对一个列表执行很频繁的成员资格检査。而in操作符的 O(n)时间复杂度对性能的影响很大,你也不能将序列转化为一个字典或者集合,因为你还需要保留原序列的元素顺序。

解决方案

假设需要给列表添加一个在该列表中不存在的元素。一个可行的方法是写这样一个函数:

def addUnique(baseList,otherList):
	auxDict = dict.fromkeys(baseList)
	for item in otherList:
		if item not in auxDict:
			baseList.append(item)
			auxDict[item] = None

如果你的代码只是在 Python 2.4下运行,那么将辅助字典换成辅助集合效果是完全一样的。

讨论

下面给出一个简单(天真?)的方式,看上去相当不错:

def addUnique_simple(baselist,otherList):
	for item in otherList:
		if item not in baseList:
			baseList.append(item)

如果列表很短的话,这个方法倒也没问题。

但是,如果列表不是很短,这个简单的方法会非常慢。当你用if item not in baseList 这样的代码进行检查时,Python只会用一种方式执行in操作:对列表 baselist 的元素进行内部的循环遍历,如果找到一个元素等于item 则返回 True,如果直到循环结束也没有发现相等的元素则返回 False。in 操作的平均执行时间是正比于1en(baseList)的。addUnique simple 执行了len(otherList)次 in操作,因此它消耗的时间正比于这两个列表长度的乘积。

而解决方案给出的 addUnique 函数,首先创建了一个辅助的字典 auxDict,这一步的时间正比于len(baseList)。然后在循环中检査 dict 的成员——这是造成巨大差异的一步,因为检查一个元素是否处于一个dict 中的时间大致是一个常数,而与 dict 中元素的数目没有关系。因此,那个for循环消耗的时间正比于len(otherList),这样,整个函数所需要的时间就正比于这两个列表的长度之和。

对于运行时间的分析还可以挖得更深一点,因为在 addUnique_simple 中 baseList 的长度并不是一个常量,每当找到一个不属于 baseList的元素,baseList的长度就会增加。但这样的分析结果不会与前面的简化版的结果有太大出入。我们可以准备一些用例进行测试。当每个列表中有10个整数且有50%的重叠时,简化版比解决方案给出的函数慢30%,这样的性能下降还可以忽略。若每个列表都有100个整数,而且仍然有 50%的重叠部分,简化版比解决方案的函数慢12倍–这种级别的减速效果就无法忽略了,而且当列表变得更长的时候,情况也变得更糟。

有时,将一个辅助的 dict和序列一起使用并封装成一个对象能提高你的应用程序的性能。但在这个例子中,必须在序列被修改时不断地维护 dict,以保证它总是和序列当前所拥有的元素保持同步。这个维护任务并不是很简单,我们有很多方法来实现同步。下面给出一种“即时”的同步方式,当需要检查某元素,或者字典的内容可能已经无法和列表内容保持同步时,我们就重新构建一个辅助 dict。由于开销很小,下面的类优化了index方法和成员检查部分的代码:

class list_with_aux_dict(list):
	def __init__(self,iterable = ()):
		list.__init__(self,iterable)
		self._dict_ok = False
	def _rebuild_dict(self):
		self.dict = {}
		for i,item in enumerate(self):
			if item not in self._dict:
				self._dict[item] = i
		self._dict_ok = True
	def __contains__(self,item):
		if not self._dict_ok:
			self._rebuild_dict()
		return item in self._dict
	def index(self,item):
		if not self._dict_ok:
			self._rebuild_dict()
		try: return self,_dict[item]
		except KeyError:raise ValueError
def _wrapMutatorMethod(methname):
	_method = getattr(list,methname)
	def wrapper(self,*args):
		#重置字典的OK标志,然后委托给真正的方法
		self._dict_ok = False
		return method(self,*args)
	#只适用于Python 2.4:wrapper.__name__ = _method.__name__
	setattr(list_with_aux_dict,methname,wrapper)
	for meth in 'setitem delitem setslice delslice iadd'.split():
		_wrapMutatorMethod('__%s__'%meth)
		for meth in 'append insert pop remove extend'.split():
			_wrapMutatorMethod(meth)
		del _wrapMethod#删除辅助函数,已经不再需要它了

list_with_aux_dict扩展了list,并将原 list的所有方法仍然委托给它,除了__contains__和 index。所有能够修改 list 的方法都被封装进了一个闭包,该闭包负责重置一个标志以确保辅助字典的有效性。Python的in操作符调用__contains__方法。除非标志被设置,否则 list_with_aux_dict的__contains__方法会重建辅助字典(标志被设置时,重建没有必要),而index方法仍然像原先一样工作。

上述 list_with_aux_dict 类并没有用帮助函数为列表的所有属性方法绑定和安装一个闭包,而是只取所需,我们也可以在 list_with_aux_dict 的主体中写出所有的 def语句来替代 wrapper 方法。但是上述代码有个重要的优点是消除了几余和重复(重复和啰嗦的代码让人生厌,而且容易滋生 bug)。Python 在自省和动态改变方面的能力给你提供了一个选择:可以创建一个 wrapper方法,用一种聪明而简练的方式,或者,如果你想避免使用被人称为黑魔法的类对象的自省和动态改变,也可以写一堆重复嗦的代码。

list_with_aux_dict 的结构很适合通常的使用模式,即对序列的修改操作一般总是集中出现,然后接着又会有一段时间序列无须被修改,但需要检查元素的成员资格。如果参数 baseList 不是一个普通的列表,而是list_with_aux_dict 的一个实例,早先展示的addUnique_simple 函数也不会因此得到任何性能上的提升,因为这个函数会交替地进行成员资格检查和序列修改。因此,类list_with_aux_dict 中过多的辅助字典的重建影响了函数的性能。(除非是针对某个特例,比如oterList 中绝大多数元素都已经在 baseList中出现过了,因此对序列的修改相比于对元素的检查,发生的次数要少得多。)

对这些成员资格的检查所做的优化有个重要的前提,即序列中的值必须是可哈希的(不然的话,它们不能被用来做字典的键或者集合的元素)。举个例子,元组的列表仍适用于本节的解决方案,但对于列表的列表,我们恐怕得另外想办法了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2332428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ZYNQ笔记(四):AXI GPIO

版本:Vivado2020.2(Vitis) 任务:使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭(两个都在PL端) 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…

实操(环境变量)Linux

环境变量概念 我们用语言写的文件编好后变成了程序,./ 运行的时候他就会变成一个进程被操作系统调度并运行,运行完毕进程相关资源被释放,因为它是一个bash的子进程,所以它退出之后进入僵尸状态,bash回收他的退出结果&…

Word / WPS 页面顶部标题 段前间距 失效 / 不起作用 / 不显示,标题紧贴页眉 问题及解决

问题描述: 在 Word 或者 WPS 里面,如果不是新的一节,而是位于新的一页首行时,不管怎么设置段前间距,始终是失效的,实际段前间距一直是零。 解决方案: 查询了很多方案均无法解决问题&#xff…

Linux自行实现的一个Shell(15)

文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…

在 Q3D 中提取汇流条电感

汇流条排简介和设计注意事项 汇流条排是用于配电的金属导体,在许多应用中与传统布线相比具有设计优势。在设计母线排时,必须考虑几个重要的因素: 低电感:高频开关内容会导致无功损耗,从而降低效率电容:管…

MySQL:事务的理解

一、CURD不加控制,会有什么问题 (1)因为,MySQL里面存的是数据,所以很有可能会被多个客户访问,所以mysqld可能一次会接受到多个关于CURD的请求。(2)且mysql内部是采用多线程来完成数…

python 基础:句子缩写

n int(input()) for _ in range(n):words input().split()result ""for word in words:result word[0].upper()print(result)知识点讲解 input()函数 用于从标准输入(通常是键盘)读取用户输入的内容。它返回的是字符串类型。例如在代码中…

Ruoyi-vue plus 5.2.2 flowble 结束节点异常错误

因业务要求, 我在结束节点的结束事件中,制作了一个归档的事件,来执行一个业务。 始终都会报错, 错误信息 ${archivTemplateListener} did not resolve to an implementation of interface org.flowable.engine.delegate.Execution…

Sublime Text使用教程(用Sublime Text编写C语言程序)

Sublime Text 是一款当下非常流行的文本编辑器,其功能强大(提供有众多的插件)、界面简洁、还支持跨平台使用(包括 Mac OS X、Linux 和 Windows)。 在程序员眼中,Sublime Text 不仅仅是一个文本编辑器&…

【1】k8s集群管理系列--包应用管理器之helm

一、helm概述 Helm核心是模板,即模板化K8s YAML文件。 通过模板实现Chart高效复用,当部署多个应用时,可以将差异化的字段进行模板化,在部署时使用-f或 者–set动态覆盖默认值,从而适配多个应用 helm工作流程&#xf…

Mysql表的操作(2)

1.去重 select distinct 列名 from 表名 2.查询时排序 select 列名 from 表名 order by 列名 asc/desc; 不影响数据库里面的数据 错误样例 : 但结果却有点出乎意料了~为什么会失败呢? 其实这是因为书写的形式不对,如果带了引号,…

智能物联网网关策略部署

实训背景 某智慧工厂需部署物联网网关,实现以下工业级安全管控需求: 设备准入控制:仅允许注册MAC地址的传感器接入(白名单:AA:BB:CC:DD:EE:FF)。协议合规性:禁止非Modbus TCP(端口…

Java学习总结-线程池

线程池是什么? 线程池就是一个可以复用线程的技术。 假若不用线程池的问题:创建新线程开销很大,不能来一个任务就就创建一个新线程。 如何创建线程池对象? 方法一:使用ExecutorService的实现类ThreadPoolExecutor创…

基于CNN-BiLSTM-GRU的深度Q网络(Deep Q-Network,DQN)求解移动机器人路径规划,MATLAB代码

一、深度Q网络(Deep Q-Network,DQN)介绍 1、背景与动机 深度Q网络(DQN)是深度强化学习领域的里程碑算法,由DeepMind于2013年提出。它首次在 Atari 2600 游戏上实现了超越人类的表现,解决了传统…

CVE-2025-29927 Next.js 中间件鉴权绕过漏洞

Next.js Next.js 是一个基于 React 的现代 Web 开发框架,用来构建高性能、可扩展的 Web 应用和网站。 CVE-2025-29927 Next.js 中间件鉴权绕过漏洞 CVE-2025-29927是Next.js框架中的一个授权绕过漏洞,允许攻击者通过特制的HTTP请求绕过在中间件中执行…

数据结构(五)——AVL树(平衡二叉搜索树)

目录 前言 AVL树概念 AVL树的定义 AVL树的插入 右旋转 左旋转 左右双旋 右左双旋 插入代码如下所示 AVL树的查找 AVL树的遍历 AVL树的节点个数以及高度 判断平衡 AVL树代码如下所示 小结 前言 前面我们在数据结构中介绍了二叉搜索树,其中提到了二叉搜…

C++类型转换详解

目录 一、内置 转 内置 二、内置 转 自定义 三、自定义 转 内置 四、自定义 转 自定义 五、类型转换规范化 1.static_case 2.reinterpret_cast 3.const_cast 4.dynamic_cast 六、RTTI 一、内置 转 内置 C兼容C语言,在内置类型之间转换规则和C语言一样的&am…

excel数据透视表大纲格式改为表格格式

现有这样一个数据透视表: 想要把他变成这样的表格格式: 操作步骤: 第一步: 效果: 第二步: 效果: 去掉分类汇总: 效果: 去掉展开/折叠按钮: 操作方式&#xf…

天梯集训+代码打卡笔记整理

1.着色问题 直接标注哪些行和列是被标注过的&#xff0c;安全格子的数量就是未标注的行*列 #include <bits/stdc.h> using namespace std;const int N 1e510; int hang[N],lie[N];int main(){int n,m;cin>>n>>m;int q;cin>>q;while(q--){int x,y;ci…

支付系统设计入门:核心账户体系架构

&#x1f449;目录 1 账户记账理论 2 账户设计 3 账户性能问题 4 账户核心架构 5 小结 第三方支付作为中立的第三方&#xff0c;截断了用户和商户的资金流&#xff0c;资金先从用户账户转移到第三方支付平台账户&#xff0c;得到双方确认后再从支付平台账户转移到商户账户。 支…