如何在Python 3中使用filter,map和reduce

Dic*_*cas 292 python reduce functional-programming filter python-3.x

filter,map并且reduce在Python 2中完美地工作.这是一个例子:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55
Run Code Online (Sandbox Code Playgroud)

但是在Python 3中,我收到以下输出:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined
Run Code Online (Sandbox Code Playgroud)

如果有人能向我解释为什么会这样,我将不胜感激.

代码截图进一步明确:

Python 2和3的IDLE会话并排

nha*_*tdh 326

您可以阅读Python 3.0中的新功能中的更改.因为很多东西已经改变,所以当你从2.x移动到3.x时,你应该彻底阅读它.

这里的完整答案是文档中的引用.

视图和迭代器而不是列表

一些众所周知的API不再返回列表:

  • [...]
  • map()filter()返回迭代器.如果你真的需要一个列表,快速修复是例如list(map(...)),但更好的修复通常是使用列表理解(特别是当原始代码使用lambda时),或者重写代码以便它根本不需要列表.特别棘手的是map()函数的副作用; 正确的转换是使用常规for循环(因为创建列表只会浪费).
  • [...]

内置命令

  • [...]
  • 删除了reduce().使用,functools.reduce()如果你真的需要它; 但是,99%的时间显式for循环更具可读性.
  • [...]

  • 添加`list(map(...)`无处不在......世界上是如何帮助可读性的......`python`似乎无法处理功能组合器的渐进式/流式应用.其他语言我可以链接十几个操作一个集合,它是可读的.这里?你想要什么 - 十几种嵌套`in` ?? (15认同)
  • 如果你在命令式上下文中工作,那么for循环可能是更具可读性的选项.但是有充分的理由偏爱功能性语境 - 从中​​断回到程序性可能非常难看. (11认同)
  • 我仍然无法理解可读性参数如何导致这种变化。如果出于性能原因,我可能会理解... (4认同)
  • @javadba 您确定在“流媒体应用程序”中完全需要添加`list` 调用吗?我认为“流式传输”的意思是“根本没有创建列表;在继续下一个元素之前完全处理输入的每个元素”。 (3认同)
  • “快速修复”(阅读:hack)是使用 list(map...) 但请注意,“更好的修复”是使用列表理解来代替 - 就像 [Foo(x) for x in mylist]。这不会导致到处添加 list() ,而且长期来看可能会更好。(@javadba 仅供参考) (2认同)

Jos*_*oyd 82

函数mapfilter被故意更改为返回迭代器,并且reduce从内置和放置中删除functools.reduce.

因此,对于filtermap,您可以将它们包装起来,list()以便像以前一样查看结果.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>
Run Code Online (Sandbox Code Playgroud)

现在的建议是用生成器表达式或列表推导替换你对map和filter的使用.例:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>
Run Code Online (Sandbox Code Playgroud)

他们说for循环在99%的时间里比简化更容易阅读,但我只是坚持functools.reduce.

编辑:99%的数字直接来自Guido van Rossum撰写的What's New In Python 3.0页面.

  • 您不需要在列表推导中创建额外的功能.只需使用`[i*i*i for i in range(1,11)]` (5认同)
  • i**3也相当于i\*i\*i (5认同)
  • @Breezer实际上`i**3`将调用`i .__ pow __(3)`和`i*i*i``i .__ mul __(i).__ mul __(i)`(或类似的东西).使用int并不重要,但是对于numpy数字/自定义类,它甚至可能产生不同的结果. (5认同)
  • 你是绝对正确的.我将该函数保留在列表推导示例中,以使其看起来类似于过滤器/映射示例. (2认同)
  • 我注意到,每当我们听到“Guido 做出决定 X”时,*痛苦* 是可能的结果。这是一个很好的例子:`list(list(list(.. )))` 来做python中*已经*冗长的事情。 (2认同)

Jim*_*ard 11

作为其他答案的补充,对于上下文管理器来说,这听起来像是一个很好的用例,它将这些函数的名称重新映射到返回列表并reduce在全局名称空间中引入的函数.

快速实现可能如下所示:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func
Run Code Online (Sandbox Code Playgroud)

使用情况如下所示:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))
Run Code Online (Sandbox Code Playgroud)

哪个印刷品:

190
[1, 2]
Run Code Online (Sandbox Code Playgroud)

只是我2美分:-)

  • “python”作为一种语言是一团糟 - 但它有很多优秀的库:“numpy”、“pandas”、“statsmodels”和朋友。我一直在构建像你在这里展示的方便的库,以减少使用的痛苦。母语 - 但已经失去了精力,并且尽量不要偏离“data.frame”/“datatable”或“xarray”。但值得赞扬的是尝试.. (2认同)

小智 8

由于该reduce方法已从 Python3 的内置函数中删除,请不要忘记functools在代码中导入。请看下面的代码片段。

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 5

Map、Filter 和 Reduce 的优点之一是,当您将它们“链接”在一起来做一些复杂的事情时,它们会变得非常清晰。然而,内置语法不清晰,而且都是“倒退”的。因此,我建议使用该PyFunctional包(https://pypi.org/project/PyFunctional/)。 下面是两者的比较:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}
Run Code Online (Sandbox Code Playgroud)

PyFunctional版本

语法非常清晰。你可以说:

“我有一系列航班目的地。如果城市在字典值中,我想从中获取字典键。最后,过滤掉我在此过程中创建的空列表。”

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \
Run Code Online (Sandbox Code Playgroud)

默认Python版本

一切都倒退了。你需要说:

“好的,所以,有一个列表。我想从中过滤掉空列表。为什么?因为如果城市在字典值中,我首先得到字典键。哦,我要执行此操作的列表是 Flight_destinations_dict。 ”

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
Run Code Online (Sandbox Code Playgroud)