我有一个列表,想要根据项的属性进行过滤。
以下哪个更好(可读性、性能、其他原因)?
xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)
我有一个列表,想要根据项的属性进行过滤。
以下哪个更好(可读性、性能、其他原因)?
xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)
filter
+lambda
更清晰,但请使用您认为更容易的方式。filter
的速度。def
还是lambda
创建的),filter
很可能比列表推导要慢。这几乎肯定不足以影响性能,并且在发现代码成为瓶颈之前,您不应该过多考虑性能,但是差异将存在。value
)。这比访问局部变量慢,在Python 2.x中,列表推导只访问局部变量。如果您使用的是Python 3.x,则列表推导运行在单独的函数中,因此它也将通过闭包访问value
,并且该差异将不适用。def filterbyvalue(seq, value):
for el in seq:
if el.attribute==value: yield el
然后在你的主要代码中(这里可读性真正重要),你用一个有意义的函数名替换了列表解析和过滤器。
[]
替换为生成器表达式中的 ()
,就可以将其替换为生成器表达式。同时,我同意列表推导式更加美观。 - Wayne Wernerfilter
更快的情况。 - Duncan[i for i in list if i.attribute == value]
可以更清晰地看出所有行为都在表面上发生,而不是在filter函数内部。filter
和使用列表推导式一样干净。就个人而言,我认为列表推导式更具 Python 风格。) - Thomas由于任何速度差异都很小,因此使用过滤器或列表推导式取决于个人喜好。总的来说,我倾向于使用推导式(这似乎与其他大多数答案一致),但有一种情况我更喜欢使用filter
。
一个非常常见的用例是从一些可迭代对象X中提取满足谓词P(x)的值:
[x for x in X if P(x)]
但有时您希望先对值应用某些函数:
[f(x) for x in X if P(f(x))]
作为一个具体的例子,考虑。primes_cubed = [x*x*x for x in range(1000) if prime(x)]
我认为这比使用filter
看起来略好一些。但是现在考虑
prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]
在这种情况下,我们希望针对后计算出的值进行过滤
。除了计算立方体两次的问题(想象一下更昂贵的计算),还存在写表达式两次的问题,违反了DRY美学原则。在这种情况下,我倾向于使用
prime_cubes = filter(prime, [x*x*x for x in range(1000)])
[prime(i) for i in [x**3 for x in range(1000)]]
。 - viki.omega9prime_cubes = filter(prime, (x*x*x for x in range(1000)))
。生成器表达式与列表推导式很相似,但是它们以惰性方式生成元素,只在需要时才生成下一个值,因此不会占用大量内存。 - Mateen Ulhaqprime_cubes = [1]
,以节省内存和CPU循环;-) - Dennis Krupenik[]
- Mateen Ulhaq虽然 filter
可能是“更快的方式”,但 “Pythonic 方式” 是不关心这些事情,除非性能绝对关键(在这种情况下,您不会使用 Python!)。
filter
不再更快了:请参见 https://dev59.com/qHA75IYBdhLWcg3w8-HZ#74432106 - Michael Dorner我认为我应该补充一下,在Python 3中,filter()实际上是一个迭代器对象,因此您需要将过滤方法调用传递给 list() 来构建过滤后的列表。所以在Python 2中:
lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = filter(lambda num: num % 2 == 0, lst_a)
列表b和c具有相同的值,并且在完成时间上与filter()等效 [x for x in y if z] 差不多。然而,在Python 3中,这段代码将使列表c包含一个过滤器对象,而不是过滤后的列表。要想在Python 3中得到相同的结果:
lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = list(filter(lambda num: num %2 == 0, lst_a))
问题在于list()需要接收一个可迭代对象作为参数,并从该参数创建一个新的列表。这意味着在Python 3中使用filter()方法要比[x for x in y if z]方法慢最多一倍,因为你需要遍历filter()的输出以及原始列表。list
,而过滤器将返回一个filter
,您无法像操作list
一样操纵它(例如:调用len
,这在filter
返回时不起作用)。filter
中获取结果的list
,就像在.NET中执行lst.Where(i => i.something()).ToList()
一样,我很想知道它。`a = [1, 2, 3, 4, 5, 6, 7, 8]`
`f = filter(lambda x: x % 2 == 0, a)`
`lc = [i for i in a if i % 2 == 0]`
`>>> type(f)`
``
`>>> type(lc)`
``
- Adeynack我认为第二种方式更易读。它明确地告诉你意图是过滤列表。
PS: 不要使用“list”作为变量名。
Filter就是这样。它过滤掉列表中的元素。您可以在我之前提到的官方文档链接中看到相同的定义。而列表推导式则是在先前的列表上执行某些操作后产生新列表的东西。(过滤和列表推导都创建一个新列表,而不是在旧列表的原地执行操作。这里的新列表就像一个带有全新数据类型的列表。例如将整数转换为字符串等)
在您的示例中,根据定义使用过滤器比使用列表推导更好。然而,如果您想要从列表元素中检索其他属性作为新列表,在您的示例中,则可以使用列表推导。
return [item.other_attribute for item in my_list if item.attribute==value]
filter
会稍微快一些。当我需要在列表推导完成后对某些内容进行过滤时,我常常使用以下简短代码片段。这是一个filter、lambda和list的结合(也被称为猫的忠诚度和狗的清洁度)。
在本例中,我正在读取一个文件,并剔除空行、注释行以及注释后面的任何内容:
# Throw out blank lines and comments
with open('file.txt', 'r') as lines:
# From the inside out:
# [s.partition('#')[0].strip() for s in lines]... Throws out comments
# filter(lambda x: x!= '', [s.part... Filters out blank lines
# y for y in filter... Converts filter object to list
file_contents = [y for y in filter(lambda x: x != '', [s.partition('#')[0].strip() for s in lines])]
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
- Steve Jessop
filter
更易读。当你有一个简单的表达式可以直接在列表推导式中使用,但必须将其包装在 lambda 函数(或类似地由partial
或operator
函数构建等)中才能传递给filter
时,此时列表推导式胜出。 - abarnertfilter
函数的返回值是一个过滤器生成器对象,而不是一个列表。 - Matteo Ferlafilter
的具体实现并不是很易读(这并不奇怪,因为 Python 并不是真正的函数式编程语言)。在语言开发历史中,交换参数顺序可能是更好的选择,即filter(xs, lambda: x: ...)
将从左到右阅读,就像“过滤 xs 以仅保留满足条件的值”一样。可以说,推导式应该被认为更易读,因为它是从左到右可理解的(你看我做了什么吗?),并且基于 Python 的非 FP 语言属性和filter
的不太易读的实现等方面更符合“Pythonic”的风格。 - Ezekiel Victor