关于python:使用reduce()的有用代码?

Useful code which uses reduce()?

这里有没有人有在python中使用reduce()函数的有用代码?除了我们在示例中看到的通常的+和*之外,还有其他代码吗?

用gvr引用python 3000中reduce()的命运


除了+和*之外,我发现它的其他用途是和和或,但现在我们有了anyall来替换这些情况。

foldlfoldr确实在方案中提出了很多…

以下是一些可爱的用法:

把名单弄平

目标:把[[1, 2, 3], [4, 5], [6, 7, 8]]变成[1, 2, 3, 4, 5, 6, 7, 8]

1
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])

数字到数字的列表

目标:把[1, 2, 3, 4, 5, 6, 7, 8]变成12345678

丑陋,缓慢的方式:

1
int("".join(map(str, [1,2,3,4,5,6,7,8])))

漂亮的reduce方式:

1
reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)


reduce()可用于查找3个或3个以上数字的最小公倍数:

1
2
3
4
5
6
#!/usr/bin/env python
from fractions import gcd
from functools import reduce

def lcm(*args):
    return reduce(lambda a,b: a * b // gcd(a, b), args)

例子:

1
2
3
4
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560


reduce()可用于解析点名称(eval()太不安全,无法使用):

1
2
3
>>> import __main__
>>> reduce(getattr,"os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>

找到n个给定列表的交集:

1
2
3
input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]

result = reduce(set.intersection, map(set, input_list))

返回:

1
result = set([3, 4, 5])

via:python-两个列表的交集


我认为减少是一个愚蠢的命令。因此:

1
reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')


我在代码中发现的reduce的用法涉及到这样一种情况:我有一些逻辑表达式的类结构,需要将这些表达式对象的列表转换为表达式的连接。我已经有了一个函数make_and来创建给定两个表达式的连词,所以我编写了reduce(make_and,l)。(我知道名单不是空的,否则它会像reduce(make_and,l,make_true)一样。)

这正是(一些)像reduce这样的函数式程序员(或折叠函数,如通常所调用的函数)的原因。通常已经有许多二进制函数,如+*minmax、连接,在我的例子中,make_andmake_or。拥有一个EDOCX1[1]使得将这些操作提升到列表(或者树或者任何你能得到的东西,对于一般的折叠函数)变得很简单。

当然,如果经常使用某些实例化(如sum),那么您就不想继续编写reduce。但是,不必用for循环定义sum,您可以用reduce轻松地定义它。

正如其他人提到的,可读性确实是一个问题。然而,你可以争辩说,人们之所以不太清楚是因为它不是许多人知道和/或使用的功能。


您可以用以下内容替换value = json_obj['a']['b']['c']['d']['e']

1
value = reduce(dict.__getitem__, 'abcde', json_obj)

如果您已经将路径a/b/c/..作为列表。例如,使用列表中的项更改嵌套dict的dict中的值。


功能组合:如果您已经有了要连续应用的功能列表,例如:

1
2
3
4
color = lambda x: x.replace('brown', 'blue')
speed = lambda x: x.replace('quick', 'slow')
work = lambda x: x.replace('lazy', 'industrious')
fs = [str.lower, color, speed, work, str.title]

然后您可以连续地应用它们:

1
2
3
4
>>> call = lambda s, func: func(s)
>>> s ="The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'

在这种情况下,方法链接可能更易读。但有时这是不可能的,而且这种组合可能比f1(f2(f3(f4(x))))类语法更易于阅读和维护。


@Blair Conrad:你也可以使用sum实现glob/reduce,比如:

1
files = sum([glob.glob(f) for f in args], [])

这比您的两个示例中的任何一个都不冗长,完全是Python式的,而且仍然只是一行代码。

因此,为了回答最初的问题,我个人尽量避免使用reduce,因为它从来就不是真正必要的,而且我发现它比其他方法更不清楚。然而,有些人习惯于减少和倾向于列出理解(特别是Haskell程序员)。但是,如果您还没有考虑减少方面的问题,您可能不需要担心使用它。


reduce可用于支持链接属性查找:

1
reduce(getattr, ('request', 'user', 'email'), self)

当然,这相当于

1
self.request.user.email

但当您的代码需要接受任意的属性列表时,它是有用的。

(处理django模型时,任意长度的链接属性很常见。)


在对代码进行grepping之后,似乎我使用reduce的唯一目的就是计算阶乘:

1
reduce(operator.mul, xrange(1, x+1) or (1,))


当需要找到一系列类set的对象的并集或交集时,reduce非常有用。

1
2
3
4
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3}))  # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3}))  # intersection
{1}

(除了实际的set外,其中一个例子是django的q对象。)

另一方面,如果你在处理bools,你应该使用anyall

1
2
>>> any((True, False, True))
True

reduce不仅限于标量操作,还可以用于将事物分类为bucket。(这是我最常使用的reduce)。

想象一下这样一种情况,在这种情况下,您有一个对象列表,并且您希望基于对象中扁平存储的属性,以分层方式重新组织它。在下面的示例中,我使用articles函数生成了一个与XML编码报纸中的文章相关的元数据对象列表。articles生成一个XML元素列表,然后逐个映射这些元素,生成包含一些有趣信息的对象。在前端,我想让用户按章节/小节/标题浏览文章。因此,我使用reduce获取文章列表,并返回一个反映章节/小节/文章层次结构的字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from lxml import etree
from Reader import Reader

class IssueReader(Reader):
    def articles(self):
        arts = self.q('//div3')  # inherited ... runs an xpath query against the issue
        subsection = etree.XPath('./ancestor::div2/@type')
        section = etree.XPath('./ancestor::div1/@type')
        header_text = etree.XPath('./head//text()')
        return map(lambda art: {
            'text_id': self.id,
            'path': self.getpath(art)[0],
            'subsection': (subsection(art)[0] or '[none]'),
            'section': (section(art)[0] or '[none]'),
            'headline': (''.join(header_text(art)) or '[none]')
        }, arts)

    def by_section(self):
        arts = self.articles()

        def extract(acc, art):  # acc for accumulator
            section = acc.get(art['section'], False)
            if section:
                subsection = acc.get(art['subsection'], False)
                if subsection:
                    subsection.append(art)
                else:
                    section[art['subsection']] = [art]
            else:
                acc[art['section']] = {art['subsection']: [art]}
            return acc

        return reduce(extract, arts, {})

我在这里给出了这两个函数,因为我认为它显示了在处理对象时map和reduce如何能够很好地互补。同样的事情也可以通过for循环完成,…但是花一些时间在功能性语言上往往会让我从映射和减少的角度思考。

顺便说一下,如果有人能像我在extract所做的那样更好地设置属性,而您要设置的属性的父级可能还不存在,请告诉我。


我正在为一种语言编写一个撰写函数,因此我使用reduce和apply操作符构造撰写函数。

简言之,compose将一个函数列表组合成一个函数。如果我有一个分阶段应用的复杂操作,我希望将其全部组合起来,如下所示:

1
complexop = compose(stage4, stage3, stage2, stage1)

这样,我就可以将它应用到如下表达式:

1
complexop(expression)

我希望它等同于:

1
stage4(stage3(stage2(stage1(expression))))

现在,为了构建我的内部对象,我想说:

1
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))

(lambda类生成用户定义的函数,而apply则生成函数应用程序。)

现在,减少,不幸的是,折叠错误的方式,所以我结束使用,大致:

1
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))

要想知道什么是减产,请在repl中尝试这些方法:

1
2
reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))


不确定这是否是你想要的,但是你可以在谷歌上搜索源代码。

按照链接搜索google代码搜索上的"function:reduce()lang:python"

乍一看,以下项目使用reduce()

  • 莫宁
  • 应用服务器
  • 数字的
  • 科学Python

等等,但这些并不令人惊讶,因为它们是巨大的项目。

reduce的功能可以使用函数递归来完成,我猜guido认为这更显式。

更新:

由于谷歌的代码搜索在2012年1月15日停止,除了恢复常规的谷歌搜索,还有一个叫做代码片段集合的东西看起来很有前途。在回答这个(封闭式)问题时提到了许多其他资源来替代谷歌代码搜索?.

更新2(2017年5月29日):

对于Python示例(在开源代码中)来说,一个好的来源是nullege搜索引擎。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import os

files = [
    # full filenames
   "var/log/apache/errors.log",
   "home/kane/images/avatars/crusader.png",
   "home/jane/documents/diary.txt",
   "home/kane/images/selfie.jpg",
   "var/log/abc.txt",
   "home/kane/.vimrc",
   "home/kane/images/avatars/paladin.png",
]

# unfolding of plain filiname list to file-tree
fs_tree = ({}, # dict of folders
           []) # list of files
for full_name in files:
    path, fn = os.path.split(full_name)
    reduce(
        # this fucction walks deep into path
        # and creates placeholders for subfolders
        lambda d, k: d[0].setdefault(k,         # walk deep
                                     ({}, [])), # or create subfolder storage
        path.split(os.path.sep),
        fs_tree
    )[1].append(fn)

print fs_tree
#({'home': (
#    {'jane': (
#        {'documents': (
#           {},
#           ['diary.txt']
#        )},
#        []
#    ),
#    'kane': (
#       {'images': (
#          {'avatars': (
#             {},
#             ['crusader.png',
#             'paladin.png']
#          )},
#          ['selfie.jpg']
#       )},
#       ['.vimrc']
#    )},
#    []
#  ),
#  'var': (
#     {'log': (
#         {'apache': (
#            {},
#            ['errors.log']
#         )},
#         ['abc.txt']
#     )},
#     [])
#},
#[])


我使用reduce将PostgreSQL搜索向量列表与sqlachemy searchable中的||运算符连接起来:

1
2
3
4
vectors = (self.column_vector(getattr(self.table.c, column_name))
           for column_name in self.indexed_columns)
concatenated = reduce(lambda x, y: x.op('||')(y), vectors)
compiled = concatenated.compile(self.conn)

1
2
3
def dump(fname,iterable):
  with open(fname,'w') as f:
    reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)

reduce可用于获取具有最大nth元素的列表

1
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])

将返回[5,2,5,7],因为它是具有max 3rd元素的列表+


假设有一些年度统计数据存储了一个计数器列表。我们想找出不同年份每个月的最小/最大值。例如,一月份为10。2月份是15天。我们需要把结果存储在一个新的计数器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from collections import Counter

stat2011 = Counter({"January": 12,"February": 20,"March": 50,"April": 70,"May": 15,
          "June": 35,"July": 30,"August": 15,"September": 20,"October": 60,
          "November": 13,"December": 50})

stat2012 = Counter({"January": 36,"February": 15,"March": 50,"April": 10,"May": 90,
          "June": 25,"July": 35,"August": 15,"September": 20,"October": 30,
          "November": 10,"December": 25})

stat2013 = Counter({"January": 10,"February": 60,"March": 90,"April": 10,"May": 80,
          "June": 50,"July": 30,"August": 15,"September": 20,"October": 75,
          "November": 60,"December": 15})

stat_list = [stat2011, stat2012, stat2013]

print reduce(lambda x, y: x & y, stat_list)     # MIN
print reduce(lambda x, y: x | y, stat_list)     # MAX

我刚刚发现了reduce的有用用法:在不删除分隔符的情况下拆分字符串。代码完全来自程序化的博客。代码如下:

1
2
3
4
5
6
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem =="
"
else acc + [elem], re.split("(
)"
,"a
b
c
"
), [])

结果如下:

1
2
3
4
['a
'
, 'b
'
, 'c
'
, '']

请注意,它处理的边缘情况中流行的答案,所以没有。为了更深入的解释,我是重定向到原来的博客文章。


我有一个旧的pipegrep的python实现,它使用reduce和glob模块构建要处理的文件列表:

1
2
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))

当时我觉得它很方便,但实际上没有必要,因为类似的东西也很好,而且可能更易读。

1
2
3
files = []
for f in args:
    files.extend(glob.glob(f))


我有代表某种重叠间隔(基因组外显子)的对象,并使用__and__重新定义它们的交叉点:

1
2
3
4
5
6
7
class Exon:
    def __init__(self):
        ...
    def __and__(self,other):
        ...
        length = self.length + other.length  # (e.g.)
        return self.__class__(...length,...)

然后当我收集到它们(例如,在同一个基因中)时,我使用

1
intersection = reduce(lambda x,y: x&y, exons)

使用reduce()查找日期列表是否连续:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from datetime import date, timedelta


def checked(d1, d2):
   """
    We assume the date list is sorted.
    If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
    can advance to the next reduction.
    If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
    will guarantee the result produced by reduce() to be something other than
    the last date in the sorted date list.

    Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
    Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive

   """

    #if (d2 - d1).days == 1 or (d2 - d1).days == 0:  # for Definition 1
    if (d2 - d1).days == 1:                          # for Definition 2
        return d2
    else:
        return d1 + timedelta(days=-1)

# datelist = [date(2014, 1, 1), date(2014, 1, 3),
#             date(2013, 12, 31), date(2013, 12, 30)]

# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
#             date(2014, 2, 21), date(2014, 2, 22)]

datelist = [date(2014, 2, 19), date(2014, 2, 21),
            date(2014, 2, 22), date(2014, 2, 20)]

datelist.sort()

if datelist[-1] == reduce(checked, datelist):
    print"dates are consecutive"
else:
    print"dates are not consecutive"