Python程序运行太慢的一个可能的原因是没有尽可能的调用内置方法,下面通过5个例子来演示如何用内置方法提升Python程序的性能。

1. 数组求平方和

输入一个列表,要求计算出该列表中数字的的平方和。最终性能提升了1.4倍。首先创建一个长度为10000的列表。

arr = list(range(10000)) 

1.1 最常规的写法

while循环遍历列表求平方和。平均运行时间2.97毫秒。

def sum_sqr_0(arr): 
    res = 0 
    n = len(arr) 
    i = 0 
    while i < n: 
        res += arr[i] ** 2 
        i += 1 
    return res 
%timeit sum_sqr_0(arr) 
2.97 ms ± 36.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

1.2 for range代替while循环

避免i += 1的变量类型检查带来的额外开销。平均运行时间2.9毫秒。

def sum_sqr_1(arr): 
    res = 0 
    for i in range(len(arr)): 
        res += arr[i] ** 2 
    return res 
%timeit sum_sqr_1(arr) 
2.9 ms ± 137 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

1.3 for x in arr代替for range

避免arr[i]的变量类型检查带来的额外开销。平均运行时间2.59毫秒。

def sum_sqr_2(arr): 
    res = 0 
    for x in arr: 
        res += x ** 2 
    return res 
%timeit sum_sqr_2(arr) 
2.59 ms ± 89 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

1.4 sum函数套用map函数

平均运行时间2.36毫秒

def sum_sqr_3(arr): 
    return sum(map(lambda x: x**2, arr)) 
%timeit sum_sqr_3(arr) 
2.36 ms ± 15.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

1.5 sum函数套用生成器表达式

生成器表达式如果作为某个函数的参数,则可以省略掉()。平均运行时间2.35毫秒。

def sum_sqr_4(arr): 
    return sum(x ** 2 for x in arr) 
%timeit sum_sqr_4(arr) 
2.35 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

1. 6 sum函数套用列表推导式

平均运行时间2.06毫秒。

def sum_sqr_5(arr): 
    return sum([x ** 2 for x in arr]) 
%timeit sum_sqr_5(arr) 
2.06 ms ± 27.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

2. 字符串拼接

输入一个列表,要求将列表中的字符串的前3个字符都拼接为一个字符串。最终性能提升了2.1倍。

首先创建一个列表,生成10000个随机长度和内容的字符串。

from random import randint 
 
def random_letter(): 
    return chr(ord('a') + randint(0, 25)) 
 
def random_letters(n): 
    return "".join([random_letter() for _ in range(n)]) 
 
strings = [random_letters(randint(1, 10)) for _ in range(10000)] 

2.1 最常规的写法

while循环遍历列表,对字符串进行拼接。平均运行时间1.86毫秒。

def concat_strings_0(strings): 
    res = "" 
    n = len(strings) 
    i = 0 
    while i < n: 
        res += strings[i][:3] 
        i += 1 
    return res 
%timeit concat_strings_0(strings) 
1.86 ms ± 74.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

2.2 for range代替while循环

避免i += 1的变量类型检查带来的额外开销。平均运行时间1.55毫秒。

def concat_strings_1(strings): 
    res = "" 
    for i in range(len(strings)): 
        res += strings[i][:3] 
    return res 
%timeit concat_strings_1(strings) 
1.55 ms ± 32.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

2.3 for x in strings代替for range

避免strings[i]的变量类型检查带来的额外开销。平均运行时间1.32毫秒。

def concat_strings_2(strings): 
    res = "" 
    for x in strings: 
        res += x[:3] 
    return res 
%timeit concat_strings_2(strings) 
1.32 ms ± 19.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

2.4 .join方法套用生成器表达式

平均运行时间1.06毫秒。

def concat_strings_3(strings): 
    return "".join(x[:3] for x in strings) 
%timeit concat_strings_3(strings) 
1.06 ms ± 15.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

2.5 .join方法套用列表解析式

平均运行时间0.85毫秒。

def concat_strings_4(strings): 
    return "".join([x[:3] for x in strings]) 
%timeit concat_strings_4(strings) 
858 µs ± 14.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3. 筛选奇数

输入一个列表,要求筛选出该列表中的所有奇数。最终性能提升了3.6倍。

首先创建一个长度为10000的列表。

arr = list(range(10000)) 

3.1 最常规的写法

创建一个空列表res,while循环遍历列表,将奇数append到res中。平均运行时间1.03毫秒。

def filter_odd_0(arr): 
    res = [] 
    i = 0 
    n = len(arr) 
    while i < n: 
        if arr[i] % 2: 
            res.append(arr[i]) 
        i += 1 
    return res 
%timeit filter_odd_0(arr) 
1.03 ms ± 34.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3.2 for range代替while循环

避免i += 1的变量类型检查带来的额外开销。平均运行时间0.965毫秒。

def filter_odd_1(arr): 
    res = [] 
    for i in range(len(arr)): 
        if arr[i] % 2: 
            res.append(arr[i]) 
        i += 1 
    return res 
%timeit filter_odd_1(arr) 
965 µs ± 4.02 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3.3 for x in arr代替for range

避免arr[i]的变量类型检查带来的额外开销。平均运行时间0.430毫秒。

def filter_odd_2(arr): 
    res = [] 
    for x in arr: 
        if x % 2: 
            res.append(x) 
    return res 
%timeit filter_odd_2(arr) 
430 µs ± 9.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3.4 list套用filter函数

平均运行时间0.763毫秒。注意filter函数很慢,在Python 3.6里非常鸡肋。

def filter_odd_3(arr): 
    return list(filter(lambda x: x % 2, arr)) 
%timeit filter_odd_3(arr) 
763 µs ± 15.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3.5 list套用生成器表达式

平均运行时间0.398毫秒。

def filter_odd_4(arr): 
    return list((x for x in arr if x % 2)) 
%timeit filter_odd_4(arr) 
398 µs ± 16.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

3.6 带条件的列表推导式

平均运行时间0.290毫秒。

def filter_odd_5(arr): 
    return [x for x in arr if x % 2] 
%timeit filter_odd_5(arr) 
290 µs ± 5.54 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

4. 两个数组相加

输入两个长度相同的列表,要求计算出两个列表对应位置的数字之和,返回一个与输入长度相同的列表。最终性能提升了2.7倍。

首先生成两个长度为10000的列表。

arr1 = list(range(10000))  
arr2 = list(range(10000))  

4.1 最常规的写法

创建一个空列表res,while循环遍历列表,将两个列表对应的元素之和append到res中。平均运行时间1.23毫秒。

def arr_sum_0(arr1, arr2): 
    i = 0 
    n = len(arr1) 
    res = [] 
    while i < n: 
        res.append(arr1[i] + arr2[i]) 
        i += 1 
    return res 
%timeit arr_sum_0(arr1, arr2) 
1.23 ms ± 3.77 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

4.2 for range代替while循环

避免i += 1的变量类型检查带来的额外开销。平均运行时间0.997毫秒。

def arr_sum_1(arr1, arr2): 
    res = [] 
    for i in range(len(arr1)): 
        res.append(arr1[i] + arr2[i]) 
    return res 
%timeit arr_sum_1(arr1, arr2) 
997 µs ± 7.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

4.3 for i, x in enumerate代替for range

部分避免arr[i]的变量类型检查带来的额外开销。平均运行时间0.799毫秒。

def arr_sum_2(arr1, arr2): 
    res = arr1.copy() 
    for i, x in enumerate(arr2): 
        res[i] += x 
    return res 
%timeit arr_sum_2(arr1, arr2) 
799 µs ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

4.4 for x, y in zip代替for range

避免arr[i]的变量类型检查带来的额外开销。平均运行时间0.769毫秒。

def arr_sum_3(arr1, arr2): 
    res = [] 
    for x, y in zip(arr1, arr2): 
        res.append(x + y) 
    return res 
%timeit arr_sum_3(arr1, arr2) 
769 µs ± 12.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

4.5 列表推导式套用zip

平均运行时间0.462毫秒。

def arr_sum_4(arr1, arr2): 
    return [x + y for x, y in zip(arr1, arr2)] 
%timeit arr_sum_4(arr1, arr2) 
462 µs ± 3.43 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

5. 两个列表相同元素的数量

输入两个列表,要求统计两个列表相同元素的数量。其中每个列表内的元素都是不重复的。最终性能提升了5000倍。

首先创建两个列表,并将元素的顺序打乱。

from random import shuffle 
arr1 = list(range(2000)) 
shuffle(arr1) 
arr2 = list(range(1000, 3000)) 
shuffle(arr2) 

5.1 最常规的写法

while循环嵌套,判断元素arr1[i]是否等于arr2[j],平均运行时间338毫秒。

def n_common_0(arr1, arr2): 
    res = 0 
    i = 0 
    m = len(arr1) 
    n = len(arr2) 
    while i < m: 
        j = 0 
        while j < n: 
            if arr1[i] == arr2[j]: 
                res += 1 
            j += 1 
        i += 1 
    return res 
%timeit n_common_0(arr1, arr2) 
338 ms ± 7.81 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

5.2 for range代替while循环

避免i += 1的变量类型检查带来的额外开销。平均运行时间233毫秒。

def n_common_1(arr1, arr2): 
    res = 0 
    for i in range(len(arr1)): 
        for j in range(len(arr2)): 
            if arr1[i] == arr2[j]: 
                res += 1 
    return res 
%timeit n_common_1(arr1, arr2) 
233 ms ± 10.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

5.3 for x in arr代替for range

避免arr[i]的变量类型检查带来的额外开销。平均运行时间84.8毫秒。

def n_common_2(arr1, arr2): 
    res = 0 
    for x in arr1: 
        for y in arr2: 
            if x == y: 
                res += 1 
    return res 
%timeit n_common_2(arr1, arr2) 
84.8 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

5.4 使用if x in arr2代替内层循环

平均运行时间24.9毫秒。

def n_common_3(arr1, arr2): 
    res = 0 
    for x in arr1: 
        if x in arr2: 
            res += 1 
    return res 
%timeit n_common_3(arr1, arr2) 
24.9 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

5.4 使用更快的算法

将数组用.sort方法排序,再进行单层循环遍历。把时间复杂度从O(n2)降低到O(nlogn),平均运行时间0.239毫秒。

def n_common_4(arr1, arr2): 
    arr1.sort() 
    arr2.sort() 
    res = i = j = 0 
    m, n = len(arr1), len(arr2) 
    while i < m and j < n: 
        if arr1[i] == arr2[j]: 
            res += 1 
            i += 1 
            j += 1 
        elif arr1[i] > arr2[j]: 
            j += 1 
        else: 
            i += 1 
    return res 
%timeit n_common_4(arr1, arr2) 
329 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

5.5 使用更好的数据结构

将数组转为集合,求交集的长度。平均运行时间0.067毫秒。

def n_common_5(arr1, arr2): 
    return len(set(arr1) & set(arr2)) 
%timeit n_common_5(arr1, arr2) 
67.2 µs ± 755 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理

想要获取更多Python学习资料可以加QQ:2955637827私聊或加Q群630390733大家一起来学习讨论吧!

程序运行慢?你怕是写的假 Python的更多相关文章

  1. 干净win7要做几步才能运行第一个Spring MVC 写的动态web程序

    干净win7要做几步才能运行第一个Spring MVC 写的动态web程序: 1. 下载安装jdk 2. 配置Java环境变量 3. 测试一下第1,2两步是否完全成功:http://jingyan.b ...

  2. GD32电压不足时烧写程序导致程序运行异常的解决方法

    一直使用的GD32F450前段时间遇到这样一个问题,当使用J-Link供电给板子烧写程序之后,程序运行缓慢,就像运行在FLASH高速部分之外一样,但是如果使用外部供电烧写,就不会出现这个问题,而且一旦 ...

  3. [Java.File]如果写 File filesFolder = new File("/") ,在windows系统中,filesFolder 会去找哪个盘符? 答案:程序运行路径的根盘符.

    首先这段代码在Unix/Linux系统上会去找根路径,但在Windows系统上会去找C:盘还是D:盘还是其它盘呢? 其实它会去找user.dir所在盘符的根目录,user.dir即用户的当前工作目录, ...

  4. python学习笔记-python程序运行

    小白初学python,写下自己的一些想法.大神请忽略. 安装python编辑器,并配置环境(见http://www.cnblogs.com/lynn-li/p/5885001.html中 python ...

  5. linux下实现在程序运行时的函数替换(热补丁)

    声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术原作者的分享. 但是injso文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的. ...

  6. 放在NSArray、NSDictionary等容器内的对象Item,Item中的property在程序运行过程中被无故释放

    可能是被释放的property本身是OC对象而它的属性被误写成assign,例如: @interface MyItem : Object @property (nonatomic, assign) N ...

  7. 孙鑫MFC学习笔记3:MFC程序运行过程

    1.MFC中WinMain函数的位置在APPMODUL.cpp APPMODUL.cpp中是_tWinMain,其实_tWinMain是一个宏#define _tWinMain WinMain 2.全 ...

  8. Java中内存中的Heap、Stack与程序运行的关系

    堆和栈的内存管理 栈的内存管理是顺序分配的,而且定长,不存在内存回收问题:而堆 则是随机分配内存,不定长度,存在内存分配和回收的问题:堆内存和栈内存的区别可以用如下的比喻来看出:使用堆内存就象是自己动 ...

  9. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

随机推荐

  1. python应用(2):写个python程序给自己用

    用python写一个程序,然后在命令行上执行,看不到界面(UI),这种程序很常见了,叫命令行程序.然而很多人,特别是不懂程序的人,更需要看到的是一个有界面的,能通过鼠标操作的程序,毕竟已经迈进&quo ...

  2. C语言讲义——头文件

    头文件.h Dev C++可以建C项目,也可以建C++项目,下面分C和C++两种情况讨论. c.h C语言中,头文件往往不是必须的,只是描述性的文件. 因此,C项目中可以没有.h文件. cpp.h 下 ...

  3. Java基础教程——线程状态

    线程状态 JAVA定义了6种线程状态: Thread.State public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, ...

  4. Kubernetes-20:日志聚合分析系统—Loki的搭建与使用

    日志聚合分析系统--Loki 什么是Loki? Loki 是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统.它的设计非常经济高效且易于操作,因为它不会 ...

  5. Steps 组件的设计与实现

    NutUI 组件源码揭秘 前言 本文的主题是 Steps 组件的设计与实现.Steps 组件是 Steps 步骤和 Timeline 组件结合的组件,在此之前他们是两个不同的组件,在 NutUI 最近 ...

  6. 基于CFSSL工具创建CA证书,服务端证书,客户端证书

    背景描述 在局域网中部署组件时,想要通过证书来实现身份的认证,确保通信的安全性,可以通过cfssl工具来进行CA证书,服务端证书,客户端证书的创建. 目录 背景描述 部署cfssl工具 下载,上传cf ...

  7. 如何将图片、html等格式转成pdf

    const int WWidth = 600; const int HHeight = 800; List<System.Drawing.Image> AllName = new List ...

  8. SQL Alias模板

    再记不住的话就扇自己巴掌 SELECT * FROM class1 c INNER JOIN class2 p ON c.id = p.id INNER JOIN class3 s ON c.id = ...

  9. buuctf-web-[极客大挑战 2019]BuyFlag 1

    打开网页,然后发现menu中有个buyflag的连接,点进去 如果你想买这个flag ,你必须是来自CUIT的一名学生,还必须回答正确的密码.简单了解,我们查看源码,发现思路 POST方式传入两个参数 ...

  10. 学JAVA的艰难之路

    周五回家了,这两天也没有更新代码系列,很抱歉,之后会补上.今天刚到宿舍,写了会代码,想用一个数组的从键盘输入,再也弄不好了,最终在我不断的翻之前写的代码,终于是找到了一个例子,真的不容易,编程这东西, ...