Python: 获取列表子集不使用列表推导

2 投票
4 回答
1954 浏览
提问于 2025-04-17 17:19

我经常遇到这样的情况:我有一个对象的列表,还有一些相关的参数列表,比如 param1、param2、param3 等等。我想从这些对象中筛选出符合某些条件的子集。

可以用伪代码来表示这个过程:

subset = object if param1>10 and 5<param2-param3<6 and param4==1

我知道可以用列表推导式来做到这一点,但这样写起来会很复杂,别人看了可能会觉得难懂。有没有其他的办法呢?

比如,如果要把两个列表的元素一一相加,使用列表推导式写起来就很麻烦:

list1=[1,2,3,4]
list2=[10,10,10,10]

[item[0]+item[1] for item in zip(list1,list2)]
>>> [11, 12, 13, 14]

但如果用 NumPy 数组来做,这样看起来就清晰多了:

import numpy
list1=numpy.array([1,2,3,4])
list2=numpy.array([10,10,10,10])

list1 + list2
>>> array([11, 12, 13, 14])

而且当你开始对多个列表进行一些复杂操作时,清晰度会更高。

回到我最开始的问题,就是从对象列表中选择某些行:

subset = [item[0] for item in zip(object,param1,param2,param3,param4) if item[1]>10 and item[2]-item[3]>5 and item[2]-item[3]<6 and item[4]==1]

或者:

subset = [obj for obj,p1,p2,p3,p4 in zip(object,param1,param2,param3,param4) if p1>10 and p2-p3>5 and p2-p3<6 and p4==1]

虽然这样可以实现我想要的效果,但我觉得不太优雅。更别提那些不懂 Python 的人会开始说“SuperMongo 让这更简单”或者“你让我放弃 IDL 来做这个!?”

你知道有没有其他的解决方案,可以用更好看的语法来完成这个特定的任务吗?NumPy 数组能否再次帮助简化语法呢?

4 个回答

0

你可以使用 mapimap 结合 compress 来实现这个功能。

from itertools import imap, compress

compress(obj_list, imap(lambda p1, p2, p3, p4: p1 > 10 and 5 < p2 - p3 < 6 and p4 == 1, param1_list, param2_list, param3_list, param4_list))

你也可以把它封装成一个函数,这样使用起来会更简洁一些。

mask_filter(filter_function, data, masks):

    return compress(data, imap(filter_function, masks))

mask_validator = lambda *params: params[0] > 10 and 5 < params[1] - params[2] < 6 and params[3] == 1
parameter_lists = (param1_list, param2_list, param3_list, param4_list)
mask_filter(mask_validator, obj_list, *parameter_lists)
2

我觉得你的第二个例子如果适当地缩进,会变得很容易读懂:

[obj for obj, p1, p2, p3, p4 in zip(object, param1, param2, param3, param4) 
 if p1 > 10 
 and p2 - p3 > 5
 and p2 - p3 < 6
 and p4 == 1]
0

Numpy数组版本:

subset = objects[(param1>10) & (param2-param3>5) & (param2-param3<6) & (param4==1)]

但是在方括号[]里的每一个操作都会生成一个临时数组,如果你的数组很大,这可能需要一些优化。

撰写回答