Python学习笔记:6个代码性能坏习惯

一、背景

实现一个数据要求往往有多种不同的方式,殊途同归,但终究速度上仍有一定的差异。

二、坏习惯

1.不要导入根目录

无论是内置模块,还是第三方模块,使用之前都必须导入。

如果我们只是需要其中某几个函数,单独导入即可。

## 较慢
import math
%%timeit
math.sqrt(100)
# 171 ns ± 16.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

## 较快
from math import sqrt
%%timeit
sqrt(100)
# 120 ns ± 6.34 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

2.避免使用点/点链

访问对象的属性或者函数时,使用 dot 非常直观,但避免使用性能可能更佳。

# 追加一个元素、然后删除元素
my_list = [1,2,3]

## 较慢
%%timeit
my_list.append(4)
my_list.remove(4)
# 306 ns ± 38.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

## 较快
append = my_list.append
remove = my_list.remove
%%timeit
append(4)
remove(4)
# 189 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

语法上有点不直观,但是如果我们重复删除数百万次的话,可以考虑使用该技巧。

需要平衡代码的性能和可读性。

3.不要使用“+”连接字符串

字符串在 python 中是不可变的。

当使用“+”进行拼接操作时,每个子字符串都是单独操作的。

strs = ['Life', 'is', 'short', ',', 'I', 'FUCK']

## 较慢
def join_strs(strs):
    result = ''
    for s in strs:
        result += ' ' + s  # result = result + ' ' + s
    return result[1:] # 删除第一位空格
%%timeit
join_strs(strs) # 'Life is short , I FUCK'
# 1.23 µs ± 59.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

## 较快
def join_strs_better(strs):
    return ' '.join(strs)
%%timeit
join_strs_better(strs)
# 327 ns ± 55.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

对于每个子字符串,都需要申请一个内存地址,然后将它与原始字符串进行连接,这成为一种很奢侈的开销。

join() 函数事先知道所有子字符串,按最合适方式分配地址空间。

4.不要使用临时变量进行值交换

Python 内置语法支持值交换。

## 较慢
%%timeit
a = 1
b = 2
temp = a
a = b
b = temp
# 72.1 ns ± 7.31 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

## 较快
%%timeit
a = 1
b = 2
a, b = b, a
# 65.8 ns ± 8.37 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

5.使用 if-condition 短路

"短路"评估存在于许多编程语言中,Python 也是如此。

基本上,它指的是某些布尔运算符的行为,其中仅当第一个参数不足以确定整个表达式的值时才执行或评估第二个参数。

# 过滤列表:以C开头,大于30岁
my_dict = [{'name': 'Alice', 'age': 28},
           {'name': 'Bob', 'age': 23},
           {'name': 'Chris', 'age': 33},
           {'name': 'Chelsea', 'age': 2},
           {'name': 'Carol', 'age': 24}]

## 较慢
%%timeit
filtered_list = []
for person in my_dict:
    if person['name'].startswith('C') and person['age'] >= 30:
        filtered_list.append(person) 
# 1.68 µs ± 45.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

## 较快
%%timeit
filtered_list = []
for person in my_dict:
    if person['age'] >= 30 and person['name'].startswith('C'):
        filtered_list.append(person)
# 973 ns ± 57.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

在知道数据大概分布的情况下,可以通过设置过滤条件前后,控制过滤速度。

6.尽量使用for循环 避免使用while循环

Python 使用很多 CPython 来提高性能。

For Loop 具有相对较少的步骤,性能更好。

## 较慢
%%timeit
result = 0
max_number = 10

i = 0
while i < max_number:
    result += i
    i += 1
# 1.51 µs ± 63.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

## 较快
%%timeit
result = 0
max_number = 10

for i in range(max_number):
    result += i
# 1.04 µs ± 48.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

三、总结

综合考虑性能、可读性、简洁性,对代码进行评审。

参考链接:中枪了吧?六种让你的 Python 程序变慢的坏习惯

posted @   Hider1214  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示