【问题标题】:Conditional function chaining in PythonPython中的条件函数链
【发布时间】:2019-03-12 19:08:48
【问题描述】:

假设有一个函数g 我想通过链接子函数来实现。这可以通过以下方式轻松完成:

def f1(a):
    return a+1

def f2(a):
    return a*2

def f3(a):
    return a**3

g = lambda x: f1(f2(f3(x)))

但是,现在考虑一下,哪些子功能将被链接在一起,这取决于条件:具体而言,预先知道的用户指定选项。当然可以:

def g(a, cond1, cond2, cond3):

    res = a
    if cond1:
        res = f3(res)
    if cond2:
        res = f2(res)
    if cond3:
        res = f1(res)
    return res

但是,与其在每次调用函数时动态检查这些静态条件,我认为最好提前根据其组成函数定义函数g。 不幸的是,下面给出了RuntimeError: maximum recursion depth exceeded

g = lambda x: x
if cond1:
    g = lambda x: f3(g(x))
if cond2:
    g = lambda x: f2(g(x))
if cond3:
    g = lambda x: f1(g(x))

有没有在 Python 中进行这种条件链接的好方法?请注意,要链接的函数可以是 N 个,因此不能单独定义所有 2^N 个函数组合(本例中为 8 个)。

【问题讨论】:

  • g = lambda x: f(g(x)) 会炸毁你的堆栈,因为尾调用永远不会结束。
  • 你打算如何定义那些 N 函数?它们都将被命名为函数,所以f1, f2, ...fN 或者你会将它们放入dict 之类的吗?我问是因为这几乎可以定义如何有效地链接它们。
  • 你可以假设它们都是命名函数。
  • @RockyLi 是的,基本上我是想避免递归,而是使用g作为之前在代码中定义的函数对象
  • 这可以通过将条件写成g = lambda x, g=g: f3(g(x))来实现(并不是说这一定是个好主意)(默认参数捕获g的先前值,而不是递归引用新值)。

标签: python function chaining


【解决方案1】:

我找到了一种使用装饰器的解决方案。看看:

def f1(x):
    return x + 1

def f2(x):
    return x + 2

def f3(x):
    return x ** 2


conditions = [True, False, True]
functions = [f1, f2, f3]


def apply_one(func, function):
    def wrapped(x):
        return func(function(x))
    return wrapped


def apply_conditions_and_functions(conditions, functions):
    def initial(x):
        return x

    function = initial

    for cond, func in zip(conditions, reversed(functions)):
        if cond:
            function = apply_one(func, function)
    return function


g = apply_conditions_and_functions(conditions, functions)

print(g(10)) # 101, because f1(f3(10)) = (10 ** 2) + 1 = 101

条件在定义g函数时只检查一次,调用时不检查。

【讨论】:

    【解决方案2】:

    我能想到的结构上最相似的代码必须按以下方式构建,您的f1.. f3 将需要成为伪装饰器,如下所示:

    def f1(a):
        def wrapper(*args):
            return a(*args)+1
        return wrapper
    
    def f2(a):
        def wrapper(*args):
            return a(*args)*2
        return wrapper
    
    def f3(a):
        def wrapper(*args):
            return a(*args)**3
        return wrapper
    

    然后您可以将这些应用于每个功能。

    g = lambda x: x
    if cond1:
        g = f3(g)
    if cond2:
        g = f2(g)
    if cond3:
        g = f1(g)
    g(2)
    

    返回:

    # Assume cond1..3 are all True
    17 # (2**3*2+1)
    

    【讨论】:

    • 感谢您提供此解决方案。它与 Sanyash 的解决方案基本相同,使用装饰器。正如您所提到的,您的问题在结构上与问题最相似,而他使用了一个额外的抽象层,允许f1...fN 的定义保持干净。最后我接受了他的回答,因为他快了 5 英尺。我在 stackoverflow 方面不是很有经验,所以如果你认为我应该选择你的,请告诉我。
    • 不要担心选择 - 回答是关于帮助,而不是得分。我很高兴他的回答对你有用。
    猜你喜欢
    • 2016-02-26
    • 2021-04-01
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    相关资源
    最近更新 更多