3.1.5 过滤序列元素
在实际应用中,我们经常需要根据指定规则从序列中提取需要的值,或者根据规则缩短序列,即对序列做过滤。
最简单的过滤序列元素的方法是使用列表推导,示例如下:
exp_list = [1, 4, -5, 10, -7, 2, 3, -1] print([n for n in exp_list if n > 0]) print([n for n in exp_list if n < 0])
使用列表推导的一个潜在缺陷是,如果输入非常大,会产生一个非常大的结果集,占用大量内存。如果对内存比较敏感,那么可以使用生成器表达式迭代产生过滤的元素,示例如下:
pos_items = (n for n in exp_list if n > 0) for item in pos_items: print(item)
有时候,过滤规则比较复杂,如过滤的时候需要处理一些异常或者其他复杂情况,不能简单地在列表推导或者生成器表达式中表达出来。这时可以将过滤代码放到一个函数中,然后使用内置的filter()函数,示例如下:
val_list = ['1', '2', '-3', '-', '4', 'N/A', '5'] def is_int(val): try: int(val) return True except ValueError: return False new_val_list = list(filter(is_int, val_list)) print(new_val_list)
filter()函数创建了一个迭代器,因此想得到列表的话,就得像示例那样使用list()函数去转换。
通常情况下,列表推导和生成器表达式是过滤数据最简单的方式。它们还能在过滤的时候转换数据,示例如下:
import math print([math.sqrt(n) for n in exp_list if n > 0])
过滤操作的一个变种是将不符合条件的值用新的值代替,而不是丢弃它们。如在一列数据中可能不仅想找到正数,还想将不是正数的数替换成指定的数。通过将过滤条件放到条件表达式中去,可以很容易地解决这个问题,示例如下:
print([n if n > 0 else 0 for n in exp_list]) print([n if n < 0 else 0 for n in exp_list])
另外一个值得关注的过滤工具就是itertools.compress(),它以一个iterable对象和一个相对应的Boolean选择器序列作为输入参数,然后输出iterable对象中对应选择器为True的元素。当需要用另一个相关联的序列来过滤某个序列的时候,这个函数是非常有用的,示例如下:
done_work = [ 'read book', 'running', 'work', 'basketball', 'table tennis', 'bike', 'read 20 pages', 'running 5km', ] counts = [ 0, 3, 10, 4, 1, 7, 6, 1]
现在想将那些对应count值大于5的地址全部输出,可以这样写代码:
from itertools import compress more5 = [n > 5 for n in counts] print(more5) print(list(compress(done_work, more5)))
这里的关键点在于先创建一个Boolean序列指示哪些元素符合条件,然后通过compress()函数根据Boolean序列去选择输出对应位置为True的元素。
compress()函数返回的是一个迭代器。如果要得到一个列表,需要使用list()函数将结果转换为列表类型。