如何在 Python 3 中使用 filter、map 和 Reduce
- 2024-12-17 08:30:00
- admin 原创
- 158
问题描述:
这是我在 Python 2 中习惯使用filter
、map
和 的工作方式:reduce
>>> 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
然而,所有这些似乎在 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
为什么结果不同?如何让 Python 3 代码像 Python 2 代码一样工作?
另请参阅:reduce() 有什么问题?reduce
了解将其放入标准库模块而不是将其保留为内置模块的具体动机。
有关更具体的答案,请参阅在 Python 3.x 中获取 map() 以返回列表map
。
解决方案 1:
您可以在 Python 3.0 的新增功能中了解这些变化。从 2.x 升级到 3.x 时,您应该仔细阅读它,因为有很多变化。
这里的全部答案均来自文档的引用。
使用视图和迭代器代替列表
一些众所周知的 API 不再返回列表:
[...]
map()
并filter()
返回迭代器。如果您确实需要列表,那么快速修复方法是例如list(map(...))
,但更好的修复方法通常是使用列表推导(尤其是当原始代码使用 lambda 时),或者重写代码,使其根本不需要列表。特别棘手的是map()
调用函数的副作用;正确的转换是使用常规for
循环(因为创建列表只会浪费时间)。[...]
内置
[...]
已删除
reduce()
。functools.reduce()
如果确实需要,请使用它;但是,99% 的情况下,显式for
循环更易读。[...]
解决方案 2:
map
和的功能filter
被有意更改为返回迭代器,并且 reduce 被从内置函数中移除并放置在 中functools.reduce
。
因此,对于filter
和map
,您可以用它们包装起来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
>>>
现在的建议是,用生成器表达式或列表推导式替换 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]
>>>
他们说 for 循环 99% 的时间比 reduce 更容易阅读,但我还是坚持使用functools.reduce
。
编辑:99% 这个数字直接取自Guido van Rossum 撰写的“Python 3.0 的新功能”页面。
解决方案 3:
作为其他答案的补充,这听起来像是一个上下文管理器的很好用例,它将这些函数的名称重新映射到返回列表并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
用法如下:
with noiters(map):
from operator import add
print(reduce(add, range(1, 20)))
print(map(int, ['1', '2']))
打印内容:
190
[1, 2]
仅代表我个人观点 :-)
解决方案 4:
由于该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)
解决方案 5:
map、filter 和 reduce 的优点之一是,当你将它们“链接”在一起以执行复杂操作时,它们会变得非常清晰。但是,内置语法并不清晰,而且完全是“倒退的”。因此,我建议使用PyFunctional
包 ( https://pypi.org/project/PyFunctional/ )。
以下是两者的比较:
flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}
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()) \n .map(lambda x: x[0] if city in x[1] else []) \n .filter(lambda x: x != []) \n
默认 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())
)
)
解决方案 6:
from functools import reduce
def f(x):
return x % 2 != 0 and x % 3 != 0
print(*filter(f, range(2, 25)))
#[5, 7, 11, 13, 17, 19, 23]
def cube(x):
return x**3
print(*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
它按原样工作。要获取 map 的输出,请使用 * 或 list
解决方案 7:
以下是 Filter、map 和 Reduce 函数的示例。
numbers = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]
筛选:
oddNumbers = list(filter(lambda x: x%2 != 0, numbers))
print(oddNumbers)
地图:
multiplyOf2 = list(map(lambda x: x*2, numbers))
print(multiplyOf2)
由于 reduce 函数不常用,因此已从 Python 3 的内置函数中删除。但它仍然可以在 functools 模块中使用,因此您可以执行以下操作:
from functools import reduce
sumOfNumbers = reduce(lambda x,y: x+y, numbers)
print(sumOfNumbers)
解决方案 8:
functools.reduce
对于深入研究嵌套键数据结构也很有用:
from functools import reduce
from operator import getitem
# ...
megayaml = defaultdict(lambda: {})
for datum in someInput:
deepK = datum['Key'].split('/')
value = b64decode(datum['Value'])
start = megayaml[f"{some}-{complicated}-{toplevel}-{key}.irrelevant"]
parents, leaf = deepK[:-1], deepK[-1]
bottom = reduce(getitem, parents, start)
bottom[leaf] = value
我导入的operator.getitem
当然在道德上等同于lambda drill, step: drill[step]
。结合collections.defaultdict
,此代码片段允许我“展开”一棵树,并从表格转储中重建它。
是的,Guido,说“reduce 总是可以重写为 for 循环”很容易— 除非你已经有几个循环深度,而事实并非如此。Thenreduce(getitem, …)
要简单得多。
至于映射和过滤结果的惰性,有一个简单的习语:
data = filter(…)
data = map(…, data)
data = list(data) # force lazy list