2.5. 过滤列表

你已经知道了,Python拥有强大的映射列表到其它列表的能力,通过列表理解。这种能力可以同一种过滤机制合并,即列表中的某些元素被映射,而其它的被完全忽略。

例 2.13. 列表过滤语法

[mapping-expression for element in source-list if filter-expression]

这是你已经知道并且爱上的 列表理解 的一个扩展。前三分之二与列表理解一样;最后一部分,以 if 开始的,就是过滤表达式。一个过滤表达式可以是任意地计算真假值的表达式(这在Python中可能是几乎任何东西)。任何对于过滤表达式计算出真值的元素将包括在映射中。其它所有元素被忽略掉,所以它们不会进入映射表达式,并且也不被包括在输出列表中。

例 2.14. 列表过滤介绍

>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"]
>>> [elem for elem in li if len(elem) > 1]       1
['mpilgrim', 'foo']
>>> [elem for elem in li if elem != "b"]         2
['a', 'mpilgrim', 'foo', 'c', 'd', 'd']
>>> [elem for elem in li if li.count(elem) == 1] 3
['a', 'mpilgrim', 'foo', 'c']
1

这里的映射表达式很简单(它只返回每个元素的值),所以把注意力集中到过滤表达式上。当Python遍历列表时,它对每个元素执行过滤表达式;如果过滤表达式为真,元素被映射且映射表达式的值被包括在返回列表中。这里你过滤掉了所有单个字符字符串,所以留下了一个都是长字符串的列表。

2

这里你过滤掉了一个特殊值 b。注意,它会过滤掉所有出现的 b 元素,因为每次 b 被取出来,过滤表达式将是假。

3

count 是列表的方法,它返回在一个值在列表中出现的次数。你也许会认为这个过滤将消除列表中的重值,返回一个只包含了在原始列表中有着唯一值拷贝的列表。但是不是这样的,因为在原始列表中出现两次的值(在本例中, bd)被完全排除了。存在从一个列表排除重复值的方法,但是过滤不是解决办法。

例 2.15. 在 apihelper.py 中过滤列表

    methodList = [method for method in dir(object) if callable(getattr(object, method))]

这个看上去挺复杂的,它的确复杂,但是基本结构是一样的。整个过滤表达式返回一个列表,列表赋给 methodList 变量。表达式的前半部分是列表映射部分。映射表达式是一个相同的表达式,它返回每个元素的值。dir(object) 返回一个对象的属性和方法的列表,那就是你正在映射的列表。所以唯一新的部分就是在 if 后面的过滤表达式。

过滤表达式看上去很恐怖,但其实不是。你已经知道了 callablegetattr,和in了。正如你在前面部分中看到的,如果 object 是一个模块,并且 method 是在那个模块中函数的名字,表达式 getattr(object, method) 返回一个函数对象。

所以这个表达式接收一个名为 object 的对象,得到它的属性,方法,和一些其它东西的名字列表,然后过滤那个列表来除掉所有我们不关心的东西。我们执行过滤是通过得到每个属性/方法/函数的名字,然后通过 getattr 函数得到指向实际东西的引用。然后我们用 type 函数来检查对象的类型,接着查看是否那个类型是我们所关心的东西之一。特别地,我们关心方法和函数,内置的(如一个列表的 pop 方法)和用户自定义的(如 odbchelper 模块中的 buildConnectionString 函数)。我们不关心其它的属性,如内置在每一个模块中的 __name__ 属性。

进一步阅读