【问题标题】:in python is there a better way to apply a list of functions to a dictionary在 python 中有没有更好的方法将函数列表应用于字典
【发布时间】:2017-11-05 07:34:59
【问题描述】:

有没有更好的方法迭代地将函数列表应用到字典?这是我想做的一个例子。但这使用了递归。

def func1(h: dict):
    h['foo']=10
    return(h)

def func2(h: dict):
    h['bar']=100
    return(h)

def func3(h: dict):
    h['baz']=h['foo']+h['bar']
    return(h)

func3(func2(func1({'firstElement':'good'})))

产生预期的输出:

{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10}

我想以数组的形式提供函数并产生相同的输出。以下是我尝试过的方法:

def recApply(flist, h=None):
    """
    Helper Apply the list of functions iteratively over the dictionary passed
    :obj: function list each will be applied to the dictionary sequentially.
    """
    #if no dictionary passed, then set the dictionary.
    if(h == None):
        h = {}

    #iteratively call functions with dictionary as a passed parameter and returning a derived dictionary
    for f in flist:
        h = f(h)

    return(h)

flist = [func1,func2,func3]
recApply(flist,{'firstElement':'good'})

这会产生所需的输出:

{'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10}

有没有一种更易读的方法,删除 recApply 函数并希望最小化字典副本?

【问题讨论】:

  • 这不会复制任何字典;每个函数传入和传回对同一变异字典的引用。

标签: python function loops dictionary


【解决方案1】:

您无需返回字典并重新分配引用 - 可变类型通过引用传递:

def func1(h):
    h['foo'] = 10

def func2(h):
    h['bar'] = 100

def func3(h):
    h['baz'] = h['foo'] + h['bar']

start_dict = {'firstElement': 'good'}

for f in (func1, func2, func3):
    f(start_dict)

print(start_dict)
# {'firstElement': 'good', 'baz': 110, 'bar': 100, 'foo': 10}

会很好的。

【讨论】:

  • Python中的所有参数都以相同的方式传递nedbatchelder.com/text/names.html
  • @chthonicdaemon - 是的,但我们在这里讨论的是语义。您总是传递对值的引用,但实际上只能更改可变类型,从而产生“按引用传递”与“按值传递”的区别(在大多数语言中都存在)。
【解决方案2】:

reduce(或 Python 3 中的 functools.reduce)可用于将函数列表组合成单个函数。这需要你定义一个组合函数:

def compose(f, g):
    def _(x):
        return f(g(x))
    return _

还有一个恒等函数:

def identity(x):
    return x

使用这些,您可以创建一个函数g,将每个函数应用于初始输入。

g = reduce(compose, [func3, func2, func1], identity)
assert (g({'firstElement': 'good'}) ==
        {'firstElement': 'good', 'foo': 10, 'bar': 100, 'baz': 110})

请注意,这是因为func1func2func3 与纯函数足够相似,您可以使用函数monoid。松散地说,它只是意味着函数组合是关联的(compose(f, compose(g, h))compose(compose(f, g), h) 相同)并且恒等函数在组合下是中性的(compose(identity, f)compose(f, identity) 都与 f 本身相同) .

您的三个函数并不是真正的纯函数;它们更像是具有副作用的身份函数。但是,您可以将它们视为纯函数,因为您使用它们时就好像它们被定义为,例如,

def func1(h):
    h1 = {}
    h1.update(h)
    h1['foo'] = 10
    return h1

读者练习:确定我对reduce 的调用是否实际上定义了g(x) = func3(func2(func1(x)))g(x) = func1(func2(func3(x))

【讨论】:

    【解决方案3】:

    您也可以使用reduce(或Python 3 中的functools.reduce)将起始字典作为initializer 参数:

    >>> from functools import reduce  # Python 3
    >>> reduce(lambda (x, f): f(x), (func1, func2, func3), {'firstElement':'good'})
    {'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10}
    

    这会将函数一个接一个地应用到初始化器或前一个函数的结果。

    您还可以将它与functools.partial 结合起来创建一个可应用于不同输入字典的链接函数:

    >>> from functools import partial
    >>> chain = partial(reduce, lambda (x, f): f(x), (func1, func2, func3))
    >>> chain({'firstElement':'good'})
    {'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10}
    

    您可以进一步概括,使其成为 partialpartial 函数...

    >>> chainer = partial(partial, reduce, lambda (x, f): f(x))
    >>> chain = chainer((func1, func2, func3))
    >>> chain({'firstElement':'good'})
    {'bar': 100, 'baz': 110, 'firstElement': 'good', 'foo': 10}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-04
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      • 2018-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多