程序运行慢?你怕是写的假 Python

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大家一起来学习讨论吧!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容