filter映射vs列表推导

8 投票
6 回答
2760 浏览
提问于 2025-04-16 19:51

filter和map是不是跟列表推导式一样的东西?

假设我有一个这样的函数:

def fib_gen():
    a,b = 0,1
    yield 0
    yield 1
    while True:
        a,b = b,a+b
        yield b

现在我可以用列表推导式来列出斐波那契数:

a = fib_gen()
print [a.next() for i in range(int(sys.argv[1]))]

假如我只想列出偶数的斐波那契数,我可以用filter和map这样做:

a = fib_gen()
print filter(even, map(lambda x: a.next(), range(int(sys.argv[1]))))

那我怎么用列表推导式得到一样的结果呢?

6 个回答

7

filtermap 可以很容易地转换成列表推导式。

这里有一个基本的例子:

[hex(n) for n in range(0, 100) if n > 20]

这和下面的内容是一样的:

list(map(hex, filter(lambda x: x > 20, range(0, 100))))

我觉得列表推导式更容易读懂。不过如果条件变得很复杂,我还是更喜欢用 filter

所以在你的情况下:

[n for n in itertools.islice(fib_gen(), 100) if even(n)]

我在这里使用了 islice,因为这个序列是无限的。但如果你使用生成器表达式,它也会变成一个无限流:

gen = (n for n in fib_gen() if even(n))

现在你也可以用 islice 来切片这个序列:

print itertools.islice(gen, int(sys.argv[1]))

这样就不需要在推导式里使用 next 了。只要你不试图去计算这个无限序列(就像如果我们在列表推导式中省略 islice 一样),我们就可以处理你的序列。

13

filter和map跟列表推导式是一样的吗?

是的,map(f, L)[f(x) for x in L] 是等价的。filter(f, L)[x for x in L if f(x)] 也是等价的。不过,使用带有副作用的列表推导式通常是不好的(因为这样会改变生成器的状态),所以你可以使用itertools来得到一个更干净的解决方案:

 a = fib_gen()
 a = itertools.islice(a, int(sys.argv[1]))
 a = itertools.ifilter(even, a)
 print list(a)
10

你可以用生成器来存储中间结果,然后在这个结果上进行“过滤”。

fibs = (a.next() for i in whatever)
even_fibs = [num for num in fibs if num % 2 == 0]

或者你也可以用一行代码来实现:

even_fibs = [num for num in (a.next() for i in whatever) if num % 2 == 0]

需要注意的是,如果你想从一个迭代器中获取确定数量的元素,可以使用itertools.islice来代替:

from itertools import islice
fibs_max_count = int(sys.argv[1])
even_fibs = [num for num in islice(fib_gen(), fibs_max_count) if num%2 == 0]

撰写回答