更优雅的方法来避免这种单行简化的实现减少对集合的分组?

时间:2019-01-25 00:59:14

标签: python python-3.x

my evolved answer to this question中,我想出了一种方法,可以执行单行(很好,单个表达式)reduce,以创建由许多其他语言定义的groupby的结果(科特林,ObjC,Swift和Smalltalk,至少。)

我最初的尝试是:

def keyFunc(value):
    return derivative_of_value

grouped = reduce(
    lambda accum, each: accum[keyFunc(each)].append(each),
    allValues,
    defaultdict(list))

正如我在《 Aside / Tangent》中所述,问题在于lambda。 Lambda限于单个表达式。为了使其在reduce中起作用,它必须返回累积参数的修改版本。

所以我想出了以下技巧,使用元组将dict引用从归约转换为归约,还强迫(忽略)更新相同dict的副作用:

from functools import reduce
grouped = reduce(
    lambda accum, each: (accum[0], accum[0][keyFunc(each)].append(each)),
    allValues,
    (defaultdict(list), None))[0]

问题是...还有更好的方法吗? 给出,我想尝试并仍然使用单个表达式的约束在没有大量辅助函数的情况下会减少。

(我知道有时代码会告诉您一些信息,但是我对这种情况在学术上很感兴趣)

2 个答案:

答案 0 :(得分:2)

我将此内容发布到python邮件列表中,并收到了两个解决方案,这些解决方案基本上是相同的花招,但却是更为优雅的改进:

grouped = reduce(
    lambda groups, each: groups[keyFunc(each)].append(each) or groups,
    allValues,
    defaultdict(list))

这使用or跳过实际工作部分的None返回。是的,是假的。

grouped = reduce(
    lambda groups, each: (groups[keyFunc(each)].append(each), groups)[1],
    allValues,
    defaultdict(list))

第二个仍然使用元组,但只是隔离到需要它的位置,这样它就不会在其余代码中泄漏。

答案 1 :(得分:0)

不确定为什么要使用reduce甚至defaultdict做到这一点。但是,存在使用列表/字典理解的单行解决方案。例如,给定

>>> from collections import defaultdict
>>> def func1(a):
...     return str(a)
>>> b = list(range(10)) + list(range(5))
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4]

以下是使用dict的单行解决方案:

>>> {x: [y for y in b if func1(y) == x] for x in set([func1(z) for z in b])}
{'4': [4, 4], '5': [5], '2': [2, 2], '8': [8], '9': [9], '7': [7], '0': [0, 0], '3': [3, 3], '1': [1, 1], '6': [6]}

下面的解决方案可以完成工作,但是很糟糕(如@ juanpa.arrivillaga在评论中指出的那样),因为您正在创建一个可能非常大的列表,然后立即将其丢弃。参见Is it Pythonic to use list comprehensions for just side effects?

更多使用列表理解的单行解决方案(如果计算defaultdict(list)初始化行,则为2行)。

例如,使用defaultdict

>>> a = defaultdict(list)
>>> [a[func1(x)].append(x) for x in b]
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
>>> a
defaultdict(<class 'list'>, {'0': [0, 0], '1': [1, 1], '2': [2, 2], '3': [3, 3], '4': [4, 4], '5': [5], '6': [6], '7': [7], '8': [8], '9': [9]})

或使用普通字典

>>> c = {}
>>> [c[func1(x)].append(x) if c.get(func1(x)) else c.update({func1(x):[x]}) for x in b]
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
>>> c
{'0': [0, 0], '1': [1, 1], '2': [2, 2], '3': [3, 3], '4': [4, 4], '5': [5], '6': [6], '7': [7], '8': [8], '9': [9]}

1 [juanpa.arrivillaga]