写了这么多年Python代码,经常看到有同学吐槽Python运行太慢。其实Python代码优化有不少小窍门,用对了方法能让代码提速好几倍。今天我就把这些年积累的性能优化经验和你分享下,学会这些技巧,代码性能蹭蹭往上涨。
列表生成式秒杀for循环
要说Python里最常见的性能陷阱,那就是滥用for循环了。看下面这段代码:
numbers = []
for i in range(1000000):
if i % 2 == 0:
numbers.append(i * 2)
用列表生成式重写后:
numbers = [i * 2 for i in range(1000000) if i % 2 == 0]
两段代码功能完全一样,但第二种写法能快3-4倍。为啥呢?列表生成式在Python解释器层面做了优化,避免了重复的函数调用开销。
小贴士:
列表生成式不是万能的,超过3层嵌套就该考虑改用普通for循环
生成大量数据时,建议用生成器表达式替代列表生成式,避免内存爆炸
别为了用列表生成式而用,可读性更重要
字典取代if-else判断
写过Python的都知道if-else判断,但要是判断条件特别多,代码就会变得又臭又长:
def get_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
elif score >= 60:
return 'D'
else:
return 'F'
用字典改写后:
grade_map = {
90: 'A',
80: 'B',
70: 'C',
60: 'D',
0: 'F'
}
def get_grade(score):
return grade_map[max(key for key in grade_map.keys() if score >= key)]
字典查找的时间复杂度是O(1),而多个if-else的时间复杂度是O(n)。数据量大的时候,性能差距很明显。
本地变量替代全局变量
全局变量看着方便,但每次访问都要在全局命名空间里找一圈,多浪费时间:
counter = 0
def count():
global counter
for _ in range(1000000):
counter += 1
改成本地变量:
def count():
counter = 0
for _ in range(1000000):
counter += 1
return counter
第二种写法能快20%左右。Python查找变量是按照LEGB原则,本地变量最快,全局变量最慢。
小贴士:
函数内频繁访问的全局变量,建议赋值给本地变量
类方法里频繁访问的self属性也是一样的道理
别滥用global关键字,容易导致代码难维护
合理使用数据结构
选对数据结构,性能蹭蹭涨。比如判断元素是否存在,列表和集合差距就很大:
data = list(range(1000000))
x = 999999
# 列表查找
if x in data: # 很慢
print('found')
# 转成集合再查找
data_set = set(data)
if x in data_set: # 贼快
print('found')
集合做成员检查的时间复杂度是O(1),列表是O(n)。数据量大的时候,这个差距就很明显了。
小贴士:
需要去重的地方,优先用集合
需要频繁查找的地方,用字典或集合
需要保持顺序的地方,才用列表
这些优化技巧都是我踩过坑总结出来的。记住一个原则:优化之前先测试,别过早优化。代码能跑就行,真遇到性能瓶颈再来优化也不迟。
附赠一个性能测试小技巧,用装饰器测函数运行时间:
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
领取专属 10元无门槛券
私享最新 技术干货