如何高效筛选满足属性条件的Python对象实例子集?
如何更快地筛选Python列表中的元素?
问题背景
我定义了一个表示平面点的point类,并有一个包含该类实例的列表:
class point: def __init__(self,x_value, y_value): self.x = x_value self.y = y_value r1 = point(0,1) r2 = point(2,1) r3 = point(5,6) all_points = [r1,r2,r3]
需要筛选出满足x < 4且y < 4的实例,预期结果是[r1, r2]。目前用列表推导式实现:
subset = [r for r in all_points if r.x < 4 and r.y < 4]
但实际场景中要处理6万条数据且需多次执行筛选,耗时超标,想找更快的方法。
提速方案
1. 用NumPy/Pandas做向量运算(大数据量首选)
纯Python循环的效率在数据量较大时会明显不足,改用NumPy或Pandas的向量运算,底层由C实现,能大幅提升速度:
NumPy实现:
import numpy as np # 将point实例的x、y值转成结构化数组 points_np = np.array([(p.x, p.y) for p in all_points], dtype=[('x', float), ('y', float)]) # 生成筛选掩码 mask = (points_np['x'] < 4) & (points_np['y'] < 4) # 根据掩码提取原实例 subset = [all_points[i] for i in np.where(mask)[0]]
Pandas实现:
import pandas as pd # 构造包含x、y和原实例的DataFrame df = pd.DataFrame({ 'x': [p.x for p in all_points], 'y': [p.y for p in all_points], 'point_obj': all_points }) # 筛选后提取原实例 subset = df[(df['x'] < 4) & (df['y'] < 4)]['point_obj'].tolist()
这两种方法在6万条数据的多次筛选场景下,速度比纯Python列表推导式快数倍甚至一个数量级。
2. 预排序+二分查找(固定条件多次查询)
如果筛选条件固定不变,可以预先对数据排序,用二分查找缩小遍历范围:
import bisect # 预先按x值排序 sorted_points = sorted(all_points, key=lambda p: p.x) # 找到第一个x >=4的位置 x_cutoff = bisect.bisect_left(sorted_points, 4, key=lambda p: p.x) # 在前半部分筛选y <4的实例 subset = [p for p in sorted_points[:x_cutoff] if p.y < 4]
预排序只需要执行一次,后续每次筛选都能减少遍历的元素数量,适合重复执行相同筛选的场景。
3. 优化point类的属性访问(微小提速)
给point类添加__slots__,可以减少属性访问的开销:
class point: __slots__ = ['x', 'y'] # 限制属性,减少内存和访问耗时 def __init__(self,x_value, y_value): self.x = x_value self.y = y_value
这个优化的效果不算大,但在处理大量实例时能带来小幅提升。
4. 生成器表达式(内存优化为主)
如果不需要立即得到完整的列表,只是后续迭代处理结果,用生成器表达式可以节省内存,同时有轻微的速度提升:
subset_gen = (r for r in all_points if r.x < 4 and r.y < 4)
注意生成器只能迭代一次,若要多次使用需要重新生成或转为列表。
5. filter函数(与列表推导式速度接近)
filter函数的速度和列表推导式相差不大,只是写法不同:
subset = list(filter(lambda p: p.x <4 and p.y <4, all_points))
实际测试中列表推导式通常略快,但差异可以忽略。
内容的提问来源于stack exchange,提问作者Britzel
相关产品推荐
相关产品推荐

