使用 reduce() 的有用代码? [关闭]
Posted
技术标签:
【中文标题】使用 reduce() 的有用代码? [关闭]【英文标题】:Useful code which uses reduce()? [closed] 【发布时间】:2010-09-06 04:06:22 【问题描述】:这里有没有人有任何有用的代码在 python 中使用 reduce() 函数?除了我们在示例中看到的通常的 + 和 * 之外,还有其他代码吗?
参考 GvR 的 Fate of reduce() in Python 3000
【问题讨论】:
from functools import reduce
允许相同的代码在 Python 2 和 3 上运行。
【参考方案1】:
我发现除了 + 和 * 之外的其他用途是与和或,但现在我们有 any
和 all
来替换这些情况。
foldl
和 foldr
在 Scheme 中出现了很多...
这里有一些可爱的用法:
扁平化列表
目标:将[[1, 2, 3], [4, 5], [6, 7, 8]]
变成[1, 2, 3, 4, 5, 6, 7, 8]
。
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
数字列表
目标:将[1, 2, 3, 4, 5, 6, 7, 8]
变成12345678
。
丑陋,缓慢的方式:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
漂亮reduce
方式:
reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)
【讨论】:
为了展平列表,我更喜欢 list(itertools.chain(*nested_list)) sum([[1, 2, 3], [4, 5], [6, 7, 8]], []) 它对于按位运算也很有用。如果您想要按位或一组数字,例如,如果您需要将标志从列表转换为位掩码怎么办? 做一些基准测试,对于大型列表来说,“丑陋”的方式更快。timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)
需要约 0.09 秒,而timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)
需要 0.36 秒(大约慢 4 倍)。基本上,当列表变大时,乘以 10 变得昂贵,而 int 到 str 和连接保持便宜。
当然,对于小列表(大小 10)是的,那么 reduce 方法的速度要快 1.3 倍。然而,即使在这种情况下,避免 reduce 并执行简单循环甚至更快timeit.repeat('convert_digit_list_to_int(digit_list)', setup = 'digit_list = [d%10 for d in xrange(1,10)]\ndef convert_digit_list_to_int(digits):\n i = 0\n for d in digits:\n i = 10*i + d\n return i', number=100000)
需要 0.06 秒,timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,10))', number=100000)
需要 0.12 秒,将数字转换为 str 方法需要 0.16 秒。【参考方案2】:
reduce()
可用于查找Least common multiple for 3 or more numbers:
#!/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)
例子:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
【讨论】:
第二行的lcm
是什么?
@BirdJaguarIV:在答案中关注the link。 lcm()
返回两个数字的最小公倍数。【参考方案3】:
reduce()
可用于解析带点的名称(eval()
使用起来太不安全了):
>>> import __main__
>>> reduce(getattr, "os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>
【讨论】:
【参考方案4】:找到 N 个给定列表的交集:
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))
返回:
result = set([3, 4, 5])
通过:Python - Intersection of two lists
【讨论】:
see my comment to the corresponding answer【参考方案5】:我认为 reduce 是一个愚蠢的命令。因此:
reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')
【讨论】:
我也喜欢这里的讽刺【参考方案6】:我在我的代码中发现reduce
的用法涉及我有一些用于逻辑表达式的类结构并且我需要将这些表达式对象的列表转换为表达式的结合的情况。我已经有一个函数make_and
来创建给定两个表达式的连词,所以我写了reduce(make_and,l)
。 (我知道列表不是空的;否则它会像reduce(make_and,l,make_true)
。)
这正是(一些)函数式程序员喜欢reduce
(或折叠函数,因为这些函数通常被调用)的原因。通常已经有很多二进制函数,如+
、*
、min
、max
、连接,在我的例子中,还有make_and
和make_or
。拥有reduce
使得将这些操作提升到列表(或树或任何你得到的任何东西,对于一般的折叠函数)变得微不足道。
当然,如果经常使用某些实例化(如sum
),那么你就不想一直写reduce
。但是,您可以像使用reduce
一样轻松地定义它,而不是使用一些for 循环来定义sum
。
正如其他人所提到的,可读性确实是一个问题。但是,您可能会争辩说,人们觉得 reduce
不太“清晰”的唯一原因是因为它不是很多人知道和/或使用的功能。
【讨论】:
为了防止空列表你可以利用and
操作符的短路行为:L and reduce(make_and, L)
如果在这种情况下返回空列表是合适的【参考方案7】:
函数组合:如果你已经有了想要连续应用的函数列表,比如:
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]
然后您可以使用以下方法连续应用它们:
>>> 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))))
类型的语法更具可读性和可维护性。
【讨论】:
一个优点是您可以更改要在代码中应用的函数列表。【参考方案8】:您可以将value = json_obj['a']['b']['c']['d']['e']
替换为:
value = reduce(dict.__getitem__, 'abcde', json_obj)
如果您已经将路径 a/b/c/..
作为列表。例如,Change values in dict of nested dicts using items in a list。
【讨论】:
【参考方案9】:@Blair Conrad:您也可以使用 sum 实现 glob/reduce,如下所示:
files = sum([glob.glob(f) for f in args], [])
这比您的两个示例中的任何一个都不那么冗长,完全符合 Python 风格,并且仍然只有一行代码。
所以为了回答最初的问题,我个人尽量避免使用 reduce,因为它从来没有真正必要,而且我发现它比其他方法不太清楚。然而,有些人习惯于减少并更喜欢它而不是列表推导(尤其是 Haskell 程序员)。但是,如果您还没有考虑 reduce 方面的问题,您可能不需要担心使用它。
【讨论】:
sum
和 reduce
都会导致二次行为。它可以在线性时间内完成:files = chain.from_iterable(imap(iglob, args))
。尽管在这种情况下可能无关紧要,因为 glob() 访问磁盘需要时间。【参考方案10】:
reduce
可用于支持链式属性查找:
reduce(getattr, ('request', 'user', 'email'), self)
当然,这等价于
self.request.user.email
但当您的代码需要接受任意属性列表时,它会很有用。
(在处理 Django 模型时,任意长度的链式属性很常见。)
【讨论】:
【参考方案11】:reduce
在您需要查找类似set
的对象序列的并集或交集时很有用。
>>> reduce(operator.or_, (1, 1, 2, 1, 3)) # union
1, 2, 3
>>> reduce(operator.and_, (1, 1, 2, 1, 3)) # intersection
1
(除了实际的set
s,其中一个例子是Django's Q objects。)
另一方面,如果你正在处理bool
s,你应该使用any
和all
:
>>> any((True, False, True))
True
【讨论】:
【参考方案12】:在对我的代码进行 grepping 之后,似乎我唯一使用 reduce 的就是计算阶乘:
reduce(operator.mul, xrange(1, x+1) or (1,))
【讨论】:
然后将其替换为 math.factorial docs.python.org/2/library/math.html#math.factorial【参考方案13】:我正在为一种语言编写一个组合函数,所以我使用 reduce 和我的 apply 运算符来构造组合函数。
简而言之,compose 将一个函数列表组合成一个函数。如果我有一个分阶段应用的复杂操作,我想像这样把它们放在一起:
complexop = compose(stage4, stage3, stage2, stage1)
这样,我可以将它应用到这样的表达式:
complexop(expression)
我希望它等同于:
stage4(stage3(stage2(stage1(expression))))
现在,为了构建我的内部对象,我希望它说:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(Lambda 类构建用户自定义函数,Apply 构建函数应用。)
现在,不幸的是,reduce 折叠方式错误,所以我大致使用了:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
要弄清楚 reduce 产生了什么,请在 REPL 中尝试这些:
reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))
【讨论】:
我用compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)
到generate all possible combinations of functions for performance testing.【参考方案14】:
reduce 可用于获取具有最大第 n 个元素的列表
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] 因为它是具有最大第 3 个元素 + 的列表
【讨论】:
max(lst, key = lambda x: x[2])【参考方案15】:Reduce 不仅限于标量操作;它也可以用来将东西分类到桶中。 (这是我最常使用的 reduce)。
想象这样一种情况,您有一个对象列表,并且您想根据对象中平坦存储的属性按层次结构重新组织它。在以下示例中,我使用 articles
函数生成与 XML 编码报纸中的文章相关的元数据对象列表。 articles
生成一个 XML 元素列表,然后逐个映射它们,生成包含有关它们的一些有趣信息的对象。在前端,我想让用户按部分/子部分/标题浏览文章。所以我使用reduce
来获取文章列表并返回一个反映节/子节/文章层次结构的字典。
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 循环来完成,...但是花一些时间使用函数式语言往往会让我想到 map 和 reduce。
顺便说一句,如果有人有更好的方法来设置属性,就像我在extract
中所做的那样,您要设置的属性的父级可能还不存在,请告诉我。
【讨论】:
【参考方案16】:不确定这是否是您所追求的,但您可以search source code on Google。
点击链接在 Google 代码搜索中搜索 'function:reduce() lang:python'
乍一看以下项目使用reduce()
等等。等等,但这些都不足为奇,因为它们是巨大的项目。
reduce 的功能可以使用函数递归来完成,我猜 Guido 认为这更明确。
更新:
由于 Google 的代码搜索于 2012 年 1 月 15 日停止,除了恢复到常规的 Google 搜索之外,还有一个名为 Code Snippets Collection 的东西看起来很有希望。在回答这个(已关闭)问题Replacement for Google Code Search? 时提到了许多其他资源。
更新 2(2017 年 5 月 29 日):
Nullege search engine 是 Python 示例(在开源代码中)的良好来源。
【讨论】:
"reduce 的功能可以使用函数递归来完成" ...或者for
循环。
此外,搜索 reduce() 会产生在其代码中定义 reduce 函数的项目。您应该搜索 lang:python "reduce(" 来查找内置函数的实际用法。
@Seun Osewa:根据源代码编码风格,即使搜索lang:python "reduce("
也会找到reduce
的定义。【参考方案17】:
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']
# ),
# [])
#,
#[])
【讨论】:
您能否对这里发生的事情进行一些解释?不然用处真的一点都不明显。【参考方案18】:我在 sqlalchemy-searchable 中使用 reduce
to concatenate a list of PostgreSQL search vectors 和 ||
运算符:
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)
【讨论】:
【参考方案19】:我有一个 pipegrep 的旧 Python 实现,它使用 reduce 和 glob 模块来构建要处理的文件列表:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
当时我发现它很方便,但实际上没有必要,因为类似的东西同样好,而且可能更具可读性
files = []
for f in args:
files.extend(glob.glob(f))
【讨论】:
列表理解怎么样?这似乎是一个完美的应用程序:files = [glob.glob(f) for f in args]
实际上,@steveha,您的示例将生成扩展 glob 列表的列表,而不是与 glob 匹配的所有项目的平面列表,但您可以使用列表理解 + 总和,如@[Eli Courtwright](#16198) 指出。
好的,你是对的,对此感到抱歉。我还是不太喜欢extend/reduce/lambda/map的组合!我建议导入itertools
,使用docs.python.org/library/itertools.html 中的flatten()
配方,然后编写:files = flatten(glob.glob(f) for f in args)
(这一次,我在发布之前测试了代码,我知道它可以正常工作。)【参考方案20】:
假设有一些年度统计数据存储了计数器列表。 我们希望找到不同年份每个月的 MIN/MAX 值。 例如,对于一月,它将是 10。对于二月,它将是 15。 我们需要将结果存储在一个新的 Counter 中。
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
【讨论】:
【参考方案21】:我有代表某种重叠区间(基因组外显子)的对象,并使用__and__
重新定义了它们的交集:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
然后当我有它们的集合时(例如,在同一个基因中),我使用
intersection = reduce(lambda x,y: x&y, exons)
【讨论】:
【参考方案22】:def dump(fname,iterable):
with open(fname,'w') as f:
reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)
【讨论】:
这会丢弃x
并写入y
?如果x
很重要怎么办?
x
是上一次迭代的结果。在这种情况下,它是f.write
的输出,即写入的字符数。不太可能有用。也就是说,这段代码只是展示如何使用 reduce 遍历可迭代对象并保存到文件的一种方式。就像for y in iterable: x=f.write(y)
我明白了!但是遇到的第一个 x
是初始可迭代的元素,而不是 write
运算符的结果?此外,此代码依赖于 reduce
遇到其参数的顺序,因此如果我们将 pyspark.reduce
替换为 functools.reduce
,它将无法工作?【参考方案23】:
我刚刚发现reduce
的有用用法:在不删除分隔符的情况下拆分字符串。 The code is entirely from Programatically Speaking blog.这是代码:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
结果如下:
['a\n', 'b\n', 'c\n', '']
请注意,它可以处理 SO 中的流行答案无法处理的边缘情况。如需更深入的解释,我将您重定向到原始博客文章。
【讨论】:
【参考方案24】:使用reduce()判断日期列表是否连续:
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"
【讨论】:
以上是关于使用 reduce() 的有用代码? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章