如何使用 Python reduce 或 list comprehension 将函数列表按顺序应用于字符串?

How to apply a list of functions sequentially to a string using Python reduce or list comprehension?

问题陈述

我想将函数列表 fs = [ f, g, h ] 按顺序应用于字符串 text=' abCdEf '

类似于f( g( h( text) ) )

这可以使用以下代码轻松完成:

# initial text
text = '  abCDef   '

# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]

for f in fs:
    text = f(text)

# expected result is 'abcdef' with spaces stripped, and all lowercase
print(text)

使用functools.reduce

似乎 functools.reduce 应该在这里完成这项工作,因为它在每次迭代时“消耗”函数列表。

from functools import reduce

# I know `reduce` requires two arguments, but I don't even know
# which one to chose as text of function from the list
reduce(f(text), fs)

# first interaction should call
y = str.rstrip('   abCDef   ')  --> '    abCDef' 

# next iterations fails, because tries to call '   abCDef'() -- as a function 

不幸的是,这段代码不起作用,因为每次迭代 returns 都是一个 字符串 而不是一个函数,并且失败并返回 TypeError'str' object is not callable.

问题:是否有使用 mapreducelist comprehension 解决此问题的解决方案?

reduce 可以接受三个参数:

reduce(function, iterable, initializer)

这三个参数一般是什么?

  • function 是两个参数的函数。我们称这两个参数为 tf.
  • 第一个参数 t 将从 initializer 开始;然后将继续作为先前调用 function.
  • 的 return 值
  • 第二个参数 f 取自 iterable

我们案例中的这三个参数是什么?

  • 可迭代对象是您的函数列表;
  • 第二个参数f将成为函数之一;
  • 第一个参数t必须是文本;
  • 初始化器必须是初始文本;
  • function 的 return 必须是结果文本;
  • function(t, f) 必须是 f(t).

最后:

from functools import reduce

# initial text
text = '  abCDef   '

# list of functions to apply sequentially
fs = [str.rstrip, str.lstrip, str.lower]

result = reduce(lambda t,f: f(t), fs, text)

print(repr(result))
# 'abcdef'

你可以试试这个:

import functools
text = '  abCDef   '
fs = [str.rstrip, str.lstrip, str.lower]
text = functools.reduce(lambda store, func: func(store), fs, text)
print(text)

我认为您误解了 reduce 的工作原理。 Reduce 将可迭代对象减少为单个值。回调函数可以接受两个参数,一个存储和一个元素。

reduce 函数首先创建一个存储变量。然后,遍历可迭代对象,它使用存储变量和当前元素调用函数,并将存储更新为 returned 值。最后,函数 returns 存储值。最后一个参数是存储变量的开头。

所以在代码片段中,它循环遍历函数数组,并调用其上的相应函数。然后 lambda 将 return 处理后的值,更新存储。

由于您还要求 map 解决方案,这里有一个。我的 values 包含 single-element 可迭代对象,values[0] 具有原始值,values[i] 具有应用第一个 i 函数后的值。

text = '  abCDef   '
fs = [str.rstrip, str.lstrip, str.lower]

values = [[text]]
values += map(map, fs, values)
result = next(values[-1])

print(repr(result))  # prints 'abcdef'

但我不推荐这个。我主要是好奇我是否能做到。现在我将尝试考虑如何避免构建辅助列表。

这是一个替代解决方案,它允许您组合任意数量的函数并保存组合函数以供重复使用:

import functools as ft

def compose(*funcs):
    return ft.reduce(lambda f, g: lambda x: f(g(x)), funcs)

用法:

In [4]: strip_and_lower = compose(str.rstrip, str.lstrip, str.lower)

In [5]: strip_and_lower('  abCDef   ')
Out[5]: 'abcdef'

In [6]: strip_and_lower("  AJWEGIAJWGIAWJWGIWAJ   ")
Out[6]: 'ajwegiajwgiawjwgiwaj'

In [7]: strip_lower_title = compose(str.title, str.lower, str.strip)

In [8]: strip_lower_title("     hello world  ")
Out[8]: 'Hello World'

请注意,函数的顺序很重要;这就像数学函数组合一样工作,即 (f . g . h)(x) = f(g(h(x)) 因此函数从右到左应用。