Python内置的filter()函数能够从可迭代对象(如字典、列表)中筛选某些元素,并生成一个新的迭代器。可迭代对象是一个可以被“遍历”的Python对象,也就是说,它将按顺序返回各元素,这样我们就可以在for循环中使用它。

filter()函数的基本语法是:

filter(function, iterable)

返回一个可迭代的filter对象,可以使用list()函数将其转化为列表,这个列表包含过滤器对象中返回的所有的项。

filter()函数所提供的过滤方法,通常比用列表解析更有效,特别是当我们处理更大的数据集时。例如,列表解析会生成一个新列表,这会增加该处理的运行时间。当列表解析执行完毕它的表达式后,内存中会有两个列表。但是,filter()将生成一个简单的对象,该对象包含对原始列表的引用、提供的函数以及原始列表中位置的索引,这样操作占用的内存更少。

下面介绍filter()的不同用法。

filter()中使用特殊函数

filter()的第一个参数是一个函数,用它来决定第二个参数所引用的可迭代对象中的每一项的去留。此函数被调用后,当返回False时,第二个参数中的可迭代对象里面相应的值就会被删除。针对这个函数,可以是一个普通函数,也可以使用lambda函数,特别是当表达式不那么复杂的时候。

下面是filter()中使用lambda函数的方法:

filter(lambda item: item[] expression, iterable)

将下面的列表,用于lambda函数,根据lambda函数表达式筛选列表中的元素。

creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']

要筛选此列表以元音开头的水族馆生物的名称,lambda函数如下:

print(list(filter(lambda x: x[0].lower() in 'aeiou', creature_names)))

在这里,我们将列表中的一个项声明为x,并以x[0]的方式访问每个字符串的第一个字符,并且要将字母转化为小写,以确保将字母与'aeiou'中的字符匹配。

最后,要提供可迭代对向creature_name。与上一节一样,用list()将返回结果转化为列表表。

输出如下:

['Ashley', 'Olly']

当然,写一个函数,也能够实现类似的结果:

creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']

def names_vowels(x):
    return x[0].lower() in 'aeiou'

filtered_names = filter(names_vowels, creature_names)

print(list(filtered_names))

names_vowels函数中用一个表达式,完成了对creature_names的过滤。

同样,输出如下:

['Ashley', 'Olly']

总的来说,在filter()函数中使用lambda函数得到的结果与使用常规函数得到的结果相同。如果所要过滤数据更复杂了,还可能要使用正则表达式,这可能会提高代码的可读性。

filter()中使用None

我们也可以将None作为filter()的第一个参数,让迭代器过滤掉Python中布尔值是False的对象,比如长度为0的对象(如空列表或空字符串)或在数字上等于0的对象。

下面的示例中要过滤一个列表,去掉其中布尔值是False的元素。

aquarium_tanks = [11, False, 18, 21, "", 12, 34, 0, [], {}]

filtered_tanks = filter(None, aquarium_tanks)

这段代码在filter()中使用了None,并将aquarium_tanks列表作为可迭代项传入。将None作为第一个参数,可以检查列表中的元素是否为False

print(list(filtered_tanks))

然后再将filtered_tanks传给list()函数,这样就得到了一个列表。

从输出结果中可以看出,我们得到了想要的整数,那些布尔值是False的项都筛选掉了。

[11, 18, 21, 12, 34]

注意:如果不使用list()并打印filtered_tanks,将得到一个类似于<filter object at 0x7fafd5903240>这样的filter对象。filter对象是可迭代的,因此我们可以使用for循环它,也可以使用list()将其转换为列表。

借助None,用filter()快速地从列表中删除被认为False的项。

filter()用于复杂场景

对于复杂的数据结构,filter()也可以胜任,例如,有一个由字典组成的列表,我们不仅要遍历列表中的每项(字典), 还可能要遍历字典中的每个键值对,以便得到所有的数据。

举个例子,假设我们有水族馆里每种生物的一个列表以及每种生物的不同细节,用下面的列表显示此数据。

aquarium_creatures = [
  {"name": "sammy", "species": "shark", "tank number": "11", "type": "fish"},
  {"name": "ashley", "species": "crab", "tank number": "25", "type": "shellfish"},
  {"name": "jo", "species": "guppy", "tank number": "18", "type": "fish"},
  {"name": "jackie", "species": "lobster", "tank number": "21", "type": "shellfish"},
  {"name": "charlie", "species": "clownfish", "tank number": "12", "type": "fish"},
  {"name": "olly", "species": "green turtle", "tank number": "34", "type": "turtle"}
]

下面就写一个函数,用这个函数来过滤这些数据。为了让filter()访问每个字典和字典中的每个元素,这需要构造一个嵌套函数,如下所示:

def filter_set(aquarium_creatures, search_string):
    def iterator_func(x):
        for v in x.values():
            if search_string in v:
                return True
        return False
    return filter(iterator_func, aquarium_creatures)

定义filter_set()函数,以aquarium_creaturessearch_string作为参数。在filter_set()中,将内部函数iterator_func()作为filter()的参数。filter_set()函数将返回由filter()生成的迭代器。

iterator_func()x作为参数,它代表列表中的一个项(即单个字典)。

接下来,for循环访问字典中每个键值对,然后使用条件语句检查search_string是键值对中的值。

iterator_func函数作为filter函数的参数对象,用它对迭代对象进行筛选。例如:用filter_set()搜索字符串:

filtered_records = filter_set(aquarium_creatures, "2")

一旦函数执行完毕,过滤器对象存储在filtered_records变量中,我们将其转换为一个列表并打印:

print(list(filtered_records))

输出内容:

[{'name': 'ashley', 'species': 'crab', 'tank number': '25', 'type': 'shellfish'}, {'name': 'jackie', 'species': 'lobster', 'tank number': '21', 'type': 'shellfish'}, {'name': 'charlie', 'species': 'clownfish', 'tank number': '12', 'type': 'fish'}]

刚才的示例中,我们用filter()实现了在字典组成的列表中过滤制定字符。

结论

本文中列举了filter()函数的不同使用方法。如果你打算深入了解,请阅读《Python大学实用教程》(电子工业出版社)一书,这是针对零起点读者,并特别注重工程实践的不可多得的读物。

欢迎在我的个人网站阅读更多文章。