Python 列表推导式与 Perl 的 map/grep 是一样的吗?

20 投票
6 回答
7879 浏览
提问于 2025-04-15 14:18

我在理解Python中的列表推导式语法时遇到了一些困难,所以我开始想如何用我更熟悉的Perl来实现同样的功能。我意识到一些基本的例子(来自这个页面)在Perl中都可以用mapgrep来完成。

比如说:

(python)                            (perl)                  
S = [x**2 for x in range(10)]       @S = map { $_**2 } ( 0..9 );
V = [2**i for i in range(13)]       @V = map { 2**$_ } ( 0..12 );
M = [x for x in S if x % 2 == 0]    @M = grep { $_ % 2 == 0 } @S;

那么“列表推导式”是不是就是“对列表进行映射和/或过滤”的一个花哨的说法,还是说它还有其他的含义呢?

6 个回答

3

是的,它们基本上是一样的。

其实,Python 也有一个叫做 map 的功能:

S = map(lambda x: x**2, range(10))

这个和你上面提到的第一个例子是一样的。不过,在 Python 中,大家更喜欢用列表推导式的写法。我记得 Guido 曾说过,他有点后悔引入这种函数式的写法。

不过,真正有趣的地方在于列表推导式的下一步发展,那就是生成器。生成器可以让你返回一个迭代器——它不是一次性处理整个列表,而是每次处理一个元素,然后返回,这样你就不需要一次性把整个列表都放在内存里。这个功能非常强大。

3

它们是处理序列的“python风格”的方法,除了可以映射和过滤序列外,还能做一些其他事情,比如把一个(固定层级的)嵌套列表变成平坦的列表,举个例子:

[j for i in nested_list for j in i]

还有一个常规的 map 和 lambda 表达式无法做到的事情,就是对迭代的值进行结构性分解,比如:

[(x%y)*z for x,y,z in list_with_triplets_of_ints]

当然也有一些变通的方法,比如:

aux = lambda x,y,z: (x%y)*z
map(lambda t: aux(*t), list_with_triplets_of_ints)

但是当你需要应用的转换已经定义好时,通常使用 map 会更简单,比如:

map(int, list_of_str_values)

而不是

[int(i) for i in list_of_str_values]
15

你说得对:列表推导式其实就是对 map 和 filter 的一种简化写法(这两个词来自函数式编程的领域)。

希望下面的示例代码能帮助你理解它们是一样的:

>>> # Python 2
>>> [x**2 for x in range(10)] == map(lambda x: x**2, range(10))
True
>>> [2**i for i in range(13)] == map(lambda x: 2**x, range(13))
True
>>> S = [x**2 for x in range(10)]
>>> [x for x in S if x % 2 == 0] == filter(lambda x: x % 2 == 0, S)
True

需要注意的是,这在 Python 2.X 中是有效的,正如 SilentGhost 在评论中提到的。为了让它在 Python 3 中也能用,你需要把 map 或 filter 的调用放在 list 构造函数里,因为在 Python 3 中,map 和 filter 已经更新为返回迭代器,而不是列表。

>>> # Python 3
>>> [x**2 for x in range(10)] == list(map(lambda x: x**2, range(10)))
True
>>> [2**i for i in range(13)] == list(map(lambda x: 2**x, range(13)))
True
>>> S = [x**2 for x in range(10)]
>>> [x for x in S if x % 2 == 0] == list(filter(lambda x: x % 2 == 0, S))
True

撰写回答