- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议87:充分利用 set 的优势
假设有这么个需求,希望能找出两个不同的给定目录下相同的文件名。该怎么实现呢?一个可行的方法是列出两个目录下所有的文件名,然后逐一比较找出相同的项。实现代码如下:
import os def ListFilename(dirname,filesuffix): filelist = [] os.chdir(dirname) for files in os.listdir("."): if files.endswith(filesuffix): filelist.append(files) # 找出满足条件的后缀条件的文件加入到列表中 return filelist filelistA = ListFilename("C:\\",".log") filelistB = ListFilename("C:\\temp\\",".log") filelistA.sort() # 对列表进行排序 filelistB.sort() samefilelist=[] # 用来存放相同文件的列表 for a in filelistA: # 对列表进行循环比较 for b in filelistB: if a == b: samefilelist.append(a) print samefilelist
示例程序选择的数据结构为列表,首先对列表进行排序,然后进行逐项比较,其算法的复杂度为O(m×n),其中m、n分别为两个列表的长度。那么请读者思考一下,有没有更好的选择呢?我们来了解一下集合(set)的基本知识点。Python中集合是通过Hash算法实现的无序不重复的元素集。创建集合通过set()方法来实现。
>>> set("hello") set(['h', 'e', 'l', 'o']) >>> a = [1,2,"34",(5,6)] >>> set(a) # 方便地将列表转换为set set([(5, 6), 1, 2, '34'])
集合中常见的操作以及对应的时间复杂度如表8-5所示。
表8-5 集合常见操作及时间复杂度
对表8-5时间复杂度这列仔细分析会发现,集合操作的复杂度基本为O(n),最差的情况下时间复杂度才为O(n^2)。回过头来看本节开头的例子,你是不是会有这么一个想法:如果能够将对列表的操作改为对集合的操作,性能将会明显提高?那么事实是不是如我们所料呢?我们先来基于一些基本操作测试一下这两种数据结构在性能上的表现。
1)对list求相同的元素,set求并集。当元素规模为100的时候测试结果显示,list的耗时大约为set操作的15倍。
Python -m timeit -n 1000 "[x for x in xrange(100) if x in xrange(60, 100)]" 1000 loops, best of 3: 133 usec per loop Python -m timeit -n 1000 "set(xrange(100)).intersection(xrange(60, 100))" 1000 loops, best of 3: 8.99 usec per loop
当元素规模为1000时的测试结果显示,list耗时大约为set操作的144倍,如表8-6所示。
Python -m timeit -n 1000 "[x for x in xrange(1000) if x in xrange(600, 1000)]" 1000 loops, best of 3: 9.93 msec per loop Python -m timeit -n 1000 "set(xrange(1000)).intersection(xrange(600, 1000))" 1000 loops, best of 3: 68.9 usec per loop
表8-6 list和set在求相同元素操作时的性能比较
1)向list和set中添加元素,当元素规模为100的时候,list的耗时为set的9倍。
Python -m timeit -s "testset=set()" -s "for x in xrange(100): testset.add(x)" "for x in xrange(100): x in testset" 100000 loops, best of 3: 11.5 usec per loop Python -m timeit -s "tmp = list()" -s "for x in xrange(100): tmp.append(x)" "for x in xrange(100): x in tmp" 10000 loops, best of 3: 104 usec per loop
当元素规模为1000的时候,list的耗时约为set的89倍,如表8-7所示。
表8-7 往list和set中添加元素的情况下性能比较
Python -m timeit -s "testset=set()" -s "for x in xrange(1000): testset. add(x)" "for x in xrange(1000): x in testset" 10000 loops, best of 3: 105 usec per loop Python -m timeit -s "tmp = list()" -s "for x in xrange(1000): tmp.append(x)" "for x in xrange(1000): x in tmp" 100 loops, best of 3: 9.41 msec per loop
从表8-7所示的测试数据中可以看出,set在某些操作上明显比list更高效,实际上set的union、intersection、difference等操作要比list的迭代要快。因此如果涉及求list交集、并集或者差等问题可以转换为set来操作。因此本节最初的例子将list转换为set再求交集会更为简洁高效,可修改为print set(filelistA)&set(filelist)。修改后测试结果表明,即使规模在10左右,set的效率仍然比list高了将近3倍(读者可以自行验证)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论