Python 列表推导式与 Perl 的 map/grep 是一样的吗?
我在理解Python中的列表推导式语法时遇到了一些困难,所以我开始想如何用我更熟悉的Perl来实现同样的功能。我意识到一些基本的例子(来自这个页面)在Perl中都可以用map
或grep
来完成。
比如说:
(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 个回答
是的,它们基本上是一样的。
其实,Python 也有一个叫做 map 的功能:
S = map(lambda x: x**2, range(10))
这个和你上面提到的第一个例子是一样的。不过,在 Python 中,大家更喜欢用列表推导式的写法。我记得 Guido 曾说过,他有点后悔引入这种函数式的写法。
不过,真正有趣的地方在于列表推导式的下一步发展,那就是生成器。生成器可以让你返回一个迭代器——它不是一次性处理整个列表,而是每次处理一个元素,然后返回,这样你就不需要一次性把整个列表都放在内存里。这个功能非常强大。
它们是处理序列的“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]
你说得对:列表推导式其实就是对 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