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

python中if的效率_Python性能优化的20条建议

1、优化算法时间复杂度算法的时间复杂度对程序的执行效率影响最大,在Python中可以通过选择合适的数据结构来优化时间复杂度,如list和set查找某一个元素的时间复

1、优化算法时间复杂度

算法的时间复杂度对程序的执行效率影响最大,在Python中可以通过选择合适的数据结构来优化时间复杂度,如list和set查找某一个元素的时间复杂度分别是O(n)和O(1)。不同的场景有不同的优化方式,总得来说,一般有分治,分支界限,贪心,动态规划等思想。

2、减少冗余数据

如用上三角或下三角的方式去保存一个大的对称矩阵。在0元素占大多数的矩阵里使用稀疏矩阵表示。

3、合理使用copy与deepcopy

对于dict和list等数据结构的对象,直接赋值使用的是引用的方式,而有些情况下需要复制整个对象,这时可以使用copy包里的copy和deepcopy,这两个函数的不同之处在于后者是递归复制的。效率也不一样:(以下程序在ipython中运行)

import copy

a = range(100000)

%timeit -n 10 copy.copy(a) # 运行10次 copy.copy(a)

%timeit -n 10 copy.deepcopy(a)

10 loops, best of 3: 1.55 ms per loop

10 loops, best of 3: 151 ms per loop

timeit后面的-n表示运行的次数,后两行对应的是两个timeit的输出,下同。由此可见后者慢一个数量级。

4、使用dict或set查找元素

python dict和set都是使用hash表来实现(类似c++11标准库中unordered_map),查找元素的时间复杂度是O(1)

a = range(1000)

s = set(a)

d = dict((i,1) for i in a)

%timeit -n 10000 100 in d

%timeit -n 10000 100 in s

10000 loops, best of 3: 43.5 ns per loop

10000 loops, best of 3: 49.6 ns per loop

dict的效率略高(占用的空间也多一些)。

5、合理使用生成器(generator)和yield

%timeit -n 100 a = (i for i in range(100000))

%timeit -n 100 b = [i for i in range(100000)]

100 loops, best of 3: 1.54 ms per loop

100 loops, best of 3: 4.56 ms per loop

使用()得到的是一个generator对象,所需要的内存空间与列表的大小无关,所以效率会高一些。在具体应用上,比如set(i for i in range(100000))会比set([i for i in range(100000)])快。但是对于需要循环遍历的情况:

%timeit -n 10 for x in (i for i in range(100000)): pass

%timeit -n 10 for x in [i for i in range(100000)]: pass

10 loops, best of 3: 6.51 ms per loop

10 loops, best of 3: 5.54 ms per loop

后者的效率反而更高,但是如果循环里有break,用generator的好处是显而易见的。yield也是用于创建generator:

def yield_func(ls):

for i in ls:

yield i+1

def not_yield_func(ls):

return [i+1 for i in ls]

ls = range(1000000)

%timeit -n 10 for i in yield_func(ls):pass

%timeit -n 10 for i in not_yield_func(ls):pass

10 loops, best of 3: 63.8 ms per loop

10 loops, best of 3: 62.9 ms per loop

对于内存不是非常大的list,可以直接返回一个list,但是可读性yield更佳人个喜好python2.x内置generator功能的有xrange函数、itertools包等。

6、优化循环

循环之外能做的事不要放在循环内,比如下面的优化可以快一倍:

a = range(10000)

size_a = len(a)

%timeit -n 1000 for i in a: k = len(a)

%timeit -n 1000 for i in a: k = size_a

1000 loops, best of 3: 569 ?s per loop

1000 loops, best of 3: 256 ?s per loop

7、优化包含多个判断表达式的顺序

对于and,应该把满足条件少的放在前面,对于or,把满足条件多的放在前面。如:

a = range(2000)

%timeit -n 100 [i for i in a if 10

%timeit -n 100 [i for i in a if 1000

%timeit -n 100 [i for i in a if i % 2 == 0 and i > 1900]

%timeit -n 100 [i for i in a if i > 1900 and i % 2 == 0]

100 loops, best of 3: 287 ?s per loop

100 loops, best of 3: 214 ?s per loop

100 loops, best of 3: 128 ?s per loop

100 loops, best of 3: 56.1 ?s per loop

8、使用join合并迭代器中的字符串

In [1]: %%timeit

...: s = ''

...: for i in a:

...: s += i

...:

10000 loops, best of 3: 59.8 ?s per loop

In [2]: %%timeit

s = ''.join(a)

...:

100000 loops, best of 3: 11.8 ?s per loop

join对于累加的方式,有大约5倍的提升。

9、选择合适的格式化字符方式

s1, s2 = 'ax', 'bx'

%timeit -n 100000 'abc%s%s' % (s1, s2)

%timeit -n 100000 'abc{0}{1}'.format(s1, s2)

%timeit -n 100000 'abc' + s1 + s2

100000 loops, best of 3: 183 ns per loop

100000 loops, best of 3: 169 ns per loop

100000 loops, best of 3: 103 ns per loop

三种情况中,%的方式是最慢的,但是三者的差距并不大(都非常快)。(个人觉得%的可读性最好)

10、不借助中间变量交换两个变量的值

In [3]: %%timeit -n 10000

a,b=1,2

....: c=a;a=b;b=c;

....:

10000 loops, best of 3: 172 ns per loop

In [4]: %%timeit -n 10000

a,b=1,2

a,b=b,a

....:

10000 loops, best of 3: 86 ns per loop

使用a,b=b,a而不是c=a;a=b;b=c;来交换a,b的值,可以快1倍以上。

11、使用if is

a = range(10000)

%timeit -n 100 [i for i in a if i == True]

%timeit -n 100 [i for i in a if i is True]

100 loops, best of 3: 531 ?s per loop

100 loops, best of 3: 362 ?s per loop

使用 if is True 比 if == True 将近快一倍。

12、使用级联比较x

x, y, z = 1,2,3

%timeit -n 1000000 if x

%timeit -n 1000000 if x

1000000 loops, best of 3: 101 ns per loop

1000000 loops, best of 3: 121 ns per loop

x

13、while 1 比 while True 更快

def while_1():

n = 100000

while 1:

n -= 1

if n <&#61; 0: break

def while_true():

n &#61; 100000

while True:

n -&#61; 1

if n <&#61; 0: break

m, n &#61; 1000000, 1000000

%timeit -n 100 while_1()

%timeit -n 100 while_true()

100 loops, best of 3: 3.69 ms per loop

100 loops, best of 3: 5.61 ms per loop

while 1 比 while true快很多&#xff0c;原因是在python2.x中&#xff0c;True是一个全局变量&#xff0c;而非关键字。

14、使用**而不是pow

%timeit -n 10000 c &#61; pow(2,20)

%timeit -n 10000 c &#61; 2**20

10000 loops, best of 3: 284 ns per loop

10000 loops, best of 3: 16.9 ns per loop

**就是快10倍以上&#xff01;

15、使用 cProfile, cStringIO 和 cPickle等用c实现相同功能&#xff08;分别对应profile, StringIO, pickle&#xff09;的包

import cPickle

import pickle

a &#61; range(10000)

%timeit -n 100 x &#61; cPickle.dumps(a)

%timeit -n 100 x &#61; pickle.dumps(a)

100 loops, best of 3: 1.58 ms per loop

100 loops, best of 3: 17 ms per loop

由c实现的包&#xff0c;速度快10倍以上&#xff01;

16、使用最佳的反序列化方式

下面比较了eval, cPickle, json方式三种对相应字符串反序列化的效率&#xff1a;

import json

import cPickle

a &#61; range(10000)

s1 &#61; str(a)

s2 &#61; cPickle.dumps(a)

s3 &#61; json.dumps(a)

%timeit -n 100 x &#61; eval(s1)

%timeit -n 100 x &#61; cPickle.loads(s2)

%timeit -n 100 x &#61; json.loads(s3)

100 loops, best of 3: 16.8 ms per loop

100 loops, best of 3: 2.02 ms per loop

100 loops, best of 3: 798 ?s per loop

可见json比cPickle快近3倍&#xff0c;比eval快20多倍。

17、使用C扩展(Extension)

目前主要有CPython(python最常见的实现的方式)原生API, ctypes,Cython&#xff0c;cffi三种方式&#xff0c;它们的作用是使得Python程序可以调用由C编译成的动态链接库&#xff0c;其特点分别是&#xff1a;

CPython原生API: 通过引入Python.h头文件&#xff0c;对应的C程序中可以直接使用Python的数据结构。实现过程相对繁琐&#xff0c;但是有比较大的适用范围。

ctypes: 通常用于封装(wrap)C程序&#xff0c;让纯Python程序调用动态链接库&#xff08;Windows中的dll或Unix中的so文件&#xff09;中的函数。如果想要在python中使用已经有C类库&#xff0c;使用ctypes是很好的选择&#xff0c;有一些基准测试下&#xff0c;python2&#43;ctypes是性能最好的方式。

Cython: Cython是CPython的超集&#xff0c;用于简化编写C扩展的过程,Cython的优点是语法简洁&#xff0c;可以很好地兼容numpy等包含大量C扩展的库。Cython的使得场景一般是针对项目中某个算法或过程的优化。在某些测试中&#xff0c;可以有几百倍的性能提升。

cffi: cffi的就是ctypes在pypy&#xff08;详见下文&#xff09;中的实现&#xff0c;同进也兼容CPython。cffi提供了在python使用C类库的方式&#xff0c;可以直接在python代码中编写C代码&#xff0c;同时支持链接到已有的C类库。

使用这些优化方式一般是针对已有项目性能瓶颈模块的优化&#xff0c;可以在少量改动原有项目的情况下大幅度地提高整个程序的运行效率。

18、并行编程

因为GIL的存在&#xff0c;Python很难充分利用多核CPU的优势。但是&#xff0c;可以通过内置的模块multiprocessing实现下面几种并行模式&#xff1a;

多进程&#xff1a;对于CPU密集型的程序&#xff0c;可以使用multiprocessing的Process,Pool等封装好的类&#xff0c;通过多进程的方式实现并行计算。但是因为进程中的通信成本比较大&#xff0c;对于进程之间需要大量数据交互的程序效率未必有大的提高。

多线程&#xff1a;对于IO密集型的程序&#xff0c;multiprocessing.dummy模块使用multiprocessing的接口封装threading&#xff0c;使得多线程编程也变得非常轻松(比如可以使用Pool的map接口&#xff0c;简洁高效)。

分布式&#xff1a;multiprocessing中的Managers类提供了可以在不同进程之共享数据的方式&#xff0c;可以在此基础上开发出分布式的程序。

不同的业务场景可以选择其中的一种或几种的组合实现程序性能的优化。

19、终级大杀器&#xff1a;PyPy

PyPy是用RPython(CPython的子集)实现的Python&#xff0c;根据官网的基准测试数据&#xff0c;它比CPython实现的Python要快6倍以上。快的原因是使用了Just-in-Time(JIT)编译器&#xff0c;即动态编译器&#xff0c;与静态编译器(如gcc,javac等)不同&#xff0c;它是利用程序运行的过程的数据进行优化。由于历史原因目前pypy中还保留着GIL不过正在进行的STM项目试图将PyPy变成没有GIL的Python。如果python程序中含有C扩展(非cffi的方式)&#xff0c;JIT的优化效果会大打折扣&#xff0c;甚至比CPython慢&#xff08;比Numpy&#xff09;所以在PyPy中最好用纯Python或使用cffi扩展。随着STM,Numpy等项目的完善&#xff0c;相信PyPy将会替代CPython。

20、使用性能分析工具

除了上面在ipython使用到的timeit模块,还有cProfilecProfile的使用方式也非常简单:python -m cProfile filename.py&#xff0c; 是要运行程序的文件名&#xff0c;可以在标准输出中看到每一个函数被调用的次数和运行的时间&#xff0c;从而找到程序的性能瓶颈&#xff0c;然后可以有针对性地优化。

图片未显示&#xff0c;点击后可以关注公众号”四川CDA数据分析师“。



推荐阅读
  • 尽管使用TensorFlow和PyTorch等成熟框架可以显著降低实现递归神经网络(RNN)的门槛,但对于初学者来说,理解其底层原理至关重要。本文将引导您使用NumPy从头构建一个用于自然语言处理(NLP)的RNN模型。 ... [详细]
  • 对象自省自省在计算机编程领域里,是指在运行时判断一个对象的类型和能力。dir能够返回一个列表,列举了一个对象所拥有的属性和方法。my_list[ ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 解读MySQL查询执行计划的详细指南
    本文旨在帮助开发者和数据库管理员深入了解如何解读MySQL查询执行计划。通过详细的解析,您将掌握优化查询性能的关键技巧,了解各种访问类型和额外信息的含义。 ... [详细]
  • 基因组浏览器中的Wig格式解析
    本文详细介绍了Wiggle(Wig)格式及其在基因组浏览器中的应用,涵盖variableStep和fixedStep两种主要格式的特点、适用场景及具体使用方法。同时,还提供了关于数据值和自定义参数的补充信息。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 作为一名专业的Web前端工程师,掌握HTML和CSS的命名规范是至关重要的。良好的命名习惯不仅有助于提高代码的可读性和维护性,还能促进团队协作。本文将详细介绍Web前端开发中常用的HTML和CSS命名规范,并提供实用的建议。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 本文介绍如何使用 Python 的 xlrd 库读取 Excel 文件,并将其数据处理后存储到数据库中。通过实际案例,详细讲解了文件路径、合并单元格处理等常见问题。 ... [详细]
author-avatar
手机用户2502903213
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有