4.5. 过滤列表

如您所知,Python 具有强大的功能,可以通过列表推导式将列表映射到其他列表(第 3.6 节,“映射列表”)。这可以与过滤机制结合使用,在过滤机制中,列表中的一些元素被映射,而其他元素则被完全跳过。

以下是列表过滤语法

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

这是您熟悉和喜爱的列表推导式的扩展。前三分之二是相同的;最后一部分,从 if 开始,是过滤表达式。过滤表达式可以是任何计算结果为真或假的表达式(在 Python 中可以是几乎任何东西)。过滤表达式计算结果为真的任何元素都将包含在映射中。所有其他元素都将被忽略,因此它们永远不会通过映射表达式,也不会包含在输出列表中。

示例 4.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 的所有出现,因为每次出现时,过滤表达式都将为假。
3 count 是一个列表方法,它返回一个值在列表中出现的次数。您可能会认为此过滤器会从列表中消除重复项,返回一个仅包含原始列表中每个值的副本的列表。但它没有,因为在原始列表中出现两次的值(在本例中为 bd)被完全排除在外。有一些方法可以消除列表中的重复项,但过滤不是解决方案。

让我们回到 apihelper.py 中的这一行

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

这看起来很复杂,而且确实很复杂,但基本结构是相同的。整个过滤表达式返回一个列表,该列表被分配给 methodList 变量。表达式的前半部分是列表映射部分。映射表达式是一个恒等表达式,它返回每个元素的值。dir(object) 返回 object 的属性和方法列表——这就是您要映射的列表。所以唯一的新部分是 if 之后的过滤表达式。

过滤表达式看起来很可怕,但其实不然。您已经了解了callablegetattrin。正如您在上一节中所见,如果 object 是一个模块,而 method 是该模块中一个函数的名称,则表达式 getattr(object, method) 将返回一个函数对象。

因此,此表达式接受一个对象(名为 object)。然后,它获取该对象的属性、方法、函数以及其他一些内容的名称列表。然后,它会过滤该列表,以剔除您不关心的所有内容。您可以通过获取每个属性/方法/函数的名称,并通过 getattr 函数获取对真实事物的引用来进行剔除。然后,您检查该对象是否可调用,这将是任何方法和函数,包括内置的(如列表的 pop 方法)和用户定义的(如 odbchelper 模块的 buildConnectionString 函数)。您不关心其他属性,比如每个模块内置的 __name__ 属性。

关于过滤列表的进一步阅读