【问题标题】:How to create a function that returns a function of the product of a list of functions如何创建返回函数列表乘积的函数的函数
【发布时间】:2018-09-21 09:36:04
【问题描述】:

我想创建一个函数,它返回函数列表乘积的函数。函数列表应该是可变长度的,并且函数应该有不同的参数。

例如:

def f(a, b, **kwargs):
    return a + b

def g(c, d, **kwargs):
    return c + d

def product_function(function_list, **kwargs):
    ...
    <create function that returns product function of functions in 
     function_list>
    ...
    return <productfunction>

在上面的示例中,这将是这样的:

my_function = product_function([f,g])

这应该返回一个可以使用的函数就好像它被定义为:

def my_function(a, b, c, d):
    return f(a, b) * g(c, d)

我想用它来迭代一系列因素组合并优化这些组合的参数,以选择数据科学项目中最具预测性的一个。

【问题讨论】:

  • kwargs应该如何处理?如果您不需要它们,请不要添加它们。
  • 我假设 kwargs 应该传递给 function_list? 中的每个函数?
  • @abccd 我添加了 **kwargs 因为我认为所有参数都会输入到所有函数中。显然我根本不应该添加 **kwargs。
  • @Aran-Fey 不一定,只有参数相关的函数
  • 只是一个猜测,但这可能是因为您没有解释应该如何处理**kwargs,或者因为您没有发布自己尝试编写product_function

标签: python function functional-programming higher-order-functions


【解决方案1】:

您可以在 inspect 模块中的自省实用程序的帮助下完成此操作。

具体来说,我使用inspect.signature 来查找每个函数的位置参数和关键字参数,并使用Signature.bind_partial 来防止位置参数和关键字参数之间的冲突。下面是结合其他函数的一个函数的通用实现:

import inspect

def generic_operator_function(operator_function, default_value,
                              function_list, **kwargs):
    POSITIONALS = {inspect.Parameter.POSITIONAL_ONLY,
                   inspect.Parameter.POSITIONAL_OR_KEYWORD}
    KEYWORDS = {inspect.Parameter.POSITIONAL_OR_KEYWORD,
                inspect.Parameter.KEYWORD_ONLY}

    # if no functions were given, return the default value
    if not function_list:
        return lambda: default_value

    # for each function in the list, find out how many positional
    # arguments it accepts. Also find out which keyword arguments
    # it accepts.
    arg_maps = []
    kwarg_names = []
    for func in function_list:
        sig = inspect.signature(func)
        params = sig.parameters.values()

        # count the positional arguments and map them to
        # parameter names
        bound_args = sig.bind_partial(**kwargs).arguments
        arg_map = [param.name for param in params if param.kind in POSITIONALS
                                                  and param.name not in bound_args]
        arg_maps.append(arg_map)

        # find the names of all keyword arguments
        if any(param.kind == inspect.Parameter.VAR_KEYWORD for param in params):
            kwnames = True
        else:
            kwnames = {param.name for param in params if param.kind in KEYWORDS}
        kwarg_names.append(kwnames)

    # return a function that iterates through the function_list and
    # multiplies all results
    def combined_func(*args, **inner_kwargs):
        value = default_value

        i = 0
        for func, arg_map, kwnames in zip(function_list, arg_maps, kwarg_names):
            # if the function takes **kwargs, pass all kwargs. Otherwise, pass
            # only those that it supports.
            kw_arguments = kwargs.copy()
            kw_arguments.update(inner_kwargs)
            if kwnames is not True:
                kw_arguments = {k: v for k, v in kw_arguments.items() if k in kwnames}

            # take the next batch of arguments, but only those that aren't already
            # provided as keyword arguments
            arg_map = [arg for arg in arg_map if arg not in kw_arguments]
            numparams = len(arg_map)
            arguments = args[i:i+numparams]
            kw_arguments.update({arg: value for arg, value in zip(arg_map, arguments)})

            # call the function
            retval = func(**kw_arguments)
            value = operator_function(value, retval)

            i += numparams

        return value

    return combined_func

有了这个,你可以轻松定义一堆类似于你的product_function的函数:

import operator

def product_function(*args, **kwargs):
    return generic_operator_function(operator.mul, 1, *args, **kwargs)

def sum_function(*args, **kwargs):
    return generic_operator_function(operator.add, 0, *args, **kwargs)

def append_function(*args, **kwargs):
    return generic_operator_function(lambda x, y: x+[y], [], *args, **kwargs)
>>> my_function = product_function([f,g])
>>> my_function(1,2, 3,4)
21
>>> sum_function([f,g])(1,2, 3,4)
10
>>> append_function([f,g])(1,2, 3,4)
[3, 7]

它只正确传递每个函数支持的关键字参数:

>>> p = product_function([f,g], a=1, c=2)
>>> p(3, 4)
24

【讨论】:

  • 超级!非常感谢!
  • 感谢您的更新。是否可以给出fg 默认值的参数并且只填写一些参数,例如:product_function(a=1, c=1)
  • @Frits 已更新。请参阅最底部的示例。
  • 我的后续问题写错了,我想问一下如何实现它,这样p=product_function([f,g])然后调用p(a=1, c=2)
  • @Frits 但是bd 的值从何而来? fg 函数是否有这些参数的默认值?
猜你喜欢
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 2023-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多