Python: 可以将reduce转换为列表推导式吗,就像map、lambda和filter一样?

35

在使用Python编程时,我现在会避免使用maplambdafilter,而改用列表推导式,因为它更易读且执行速度更快。但是,reduce也可以被替换吗?

例如,一个对象有一个操作符union(),可以用于另一个对象a1.union(a2),并给出一个相同类型的第三个对象。

我有一个对象列表:

L = [a1, a2, a3, ...]

如何使用列表推导式来实现这些对象的union() , 相当于:

result = reduce(lambda a, b :a.union(b), L[1:], L[0])

1
在某些情况下:不是。但要看具体情况。请提供一个您想要查询的具体问题。 - sshashank124
1
@sshashank124 - 有任何示例吗? - mhawke
1
集合并集是一个不好的例子,因为你可以简单地执行 result = set().union(*L),这样即使 L 是一个空列表也能正常工作。无论如何,lambda a, b :a.union(b) 可以更简洁地写成 set.union,因为在 Python 中,obj.method(args)cls.method(obj, args) 是相同的。 - Eric
Guido建议使用for循环而不是reduce。他不喜欢函数式编程的结构。 - Didier A.
5个回答

21

众所周知,reduce函数在Python程序员中不是最受欢迎的

一般来说,reduce是对列表进行左折叠的操作。

在Python中,编写一个可以在可迭代对象上进行左折叠或右折叠的fold函数概念上很容易:

def fold(func, iterable, initial=None, reverse=False):
    x=initial
    if reverse:
        iterable=reversed(iterable)
    for e in iterable:
        x=func(x,e) if x is not None else e
    return x

如果没有某些可怕的黑客,这个内容无法在理解中被复制,因为理解中没有累加器类型函数。

只需使用reduce-或编写一个对您更有意义的函数。


嗨,伙计,你认为到了2023年,仍然无法在列表推导式中重新创建reduce函数这个说法是否仍然成立?不管怎样,回答很棒。 - Topde
@Topde:由于reduce生成单个值,而推导式生成列表(或集合或字典),因此仍然需要除推导式之外的一些语法。您可以生成所有要缩减的值,但这并不是完全相同的事情... - dawg
真的感谢您的回复,我想我们会继续使用reduce一段时间!在我的情况下,我有一个函数,将检查输入列中是否有任何值为空def is_null(*cols): """如果所有列中至少有一个空,则返回true。""" return reduce(Column.or, [F.col(c).isNull() for c in cols])输入列的数量可能会变化,因此这是一种动态处理的方式,您可以将其视为any('a','b',null) - Topde

8
由于列表推导式定义上生成另一个列表,因此您不能使用它来生成单个值。它们不是为了那个目的而设计的。(嗯...有一种这种恶劣的技巧 可以在旧版本的Python中使用泄漏的实现细节来实现。我甚至不会在这里复制示例代码。不要这样做。)
如果您担心 reduce()及其类似功能的风格方面的问题,请不用担心。给你的缩减命名,就没问题了。所以,虽然:
all_union = reduce(lambda a, b: a.union(b), L[1:], L[0])

这不太好:

from functools import reduce

def full_union(input):
    """ Compute the union of a list of sets """
    return reduce(set.union, input[1:], input[0])

result = full_union(L)

非常清晰明了。

如果你担心速度问题,可以查看 toolzcytoolz 包,它们分别是“快速”和“疯狂快速”的。在处理大型数据集时,它们通常能让你避免多次处理数据或一次性加载整个数据集到内存中,与列表推导式相比。


2
为了使 reduce() 表达式本身易读,将第一个参数不设为 lambda。例如:reduce(set.union, <list of sets>)。有时这需要您在调用 reduce 之外定义(因此命名)运算符。 - Jordan

5
reduce 的常见用途之一是将列表的列表平铺。您可以改用列表推导式实现。
L = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

使用reduce

from functools import reduce  # python 3
flattened = reduce(lambda x, y: x + y, L)

print(flattened)

[1, 2, 3, 2, 3, 4, 3, 4, 5]

使用列表推导式

flattened = [item for sublist in L for item in sublist]

print(flattened)

[1, 2, 3, 2, 3, 4, 3, 4, 5]

如果您的问题可以通过对平坦列表进行操作来解决,那么这是一个有效的替代方法。请比较以下针对给定示例的一行代码:
all_union = reduce(lambda a, b: set(a).union(set(b)), L)

{1, 2, 3, 4, 5}

all_union = set([item for sublist in L for item in sublist])

{1, 2, 3, 4, 5}

3
使用sum(L, [])。尽管如此,使用求和/列表推导式会创建一个“完整长度”的列表,而reduce(operator.or_, map(set, L), set())则不会。 - Andy Hayden
参见:https://dev59.com/qnNA5IYBdhLWcg3wdtld - Karl Knechtel

4
不完全正确。列表推导式更类似于mapfilter

0

对于联合类型来说,这不是最易读的 reduce 方法,但可以使用赋值表达式在单个表达式中解决它:

In [51]: L = [{1}, {2}, {3}, {4}, {5}]

In [52]: reduce(lambda a, b: a.union(b), L[1:], L[0])
Out[52]: {1, 2, 3, 4, 5}

In [53]: (s := set(), [s := s.union(x) for x in L][-1])[1]
Out[53]: {1, 2, 3, 4, 5}

这怎么比reduce更易读呢? - Topde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接