什么装饰器将允许我们调用一个函数,其中每个单独的参数都用括号括起来?

What decorator will allow us to call a function where each individual argument is wrapped in parentheses?

提问人:Toothpick Anemone 提问时间:9/18/2022 更新时间:9/19/2022 访问量:192

问:

我们从函数开始,如下所示:

def funky_function(x1, x2, x3, x4, /):
    return ", ".join(" ".join(str(x).split()) for x in [x1, x2, x3, x4])

修饰函数后,我们应该在每个调用参数周围写括号。

r = f(1)(2)(3)(4) # GOOD
# r = f(1, 2, 3, 4) # BAD

一个潜在的应用是泛化装饰器。
某些装饰器只处理单参数函数。

我们可能需要一个新的装饰器,它适用于多参数函数。

例如,您可以通过泛化 来实现函数重载(多重调度)。其他人已经实施了多种方法;所以这不是我的问题。但是,我想提供一些示例应用程序作为动力。functools.singledispatch

from functools import singledispatch

    @singledispatch
    def fun(arg):
        pass  

    @fun.register
    def _(arg: int):
        print("I am processing an integer")
   
    @fun.register
    def _(arg: list):
        print("I am processing a list")

我尝试编写一些代码来完成此任务,但它没有表现出所需的行为。理想情况下,修饰函数会变成一个参数的函数,该参数返回另一个函数。

return_value = f(1)(2)(3)(4)  

下面是一些代码:

from functools import *
from inspect import *

class SeperatorHelper:
    def __init__(self, func):
        """`func` should be callable"""
        assert(callable(func))
        self._func = func
    def __call__(self, arg):
        return type(self)(partial(self._func, arg))

def seperate_args(old_func, /):
    """
        This is a decorator

        @seperate_args
        def foo(x1, x2, x3, x4, /):
            pass

       +------------------+------------------+
       |       NEW        |       OLD        |
       +------------------+------------------+
       | f(1)(2)(3)(4)    | f(1, 2, 3, 4)    |
       +------------------+------------------+
    """
    new_func = SeperatorHelper(old_func)
    new_func = wraps(old_func)(new_func)
    return new_func

#######################################################
# BEGIN TESTING
#######################################################

@seperate_args
def funky_function(x1, x2, x3, x4, /):
    return ", ".join(" ".join(str(x).split()) for x in [x1, x2, x3, x4])

print("signature == ", signature(funky_function))

func_calls = [
    "funky_function(1)(2)",
    "funky_function(1)(2)(3)(4)",
    "funky_function(1)(2)(3)(4)("extra arg")",
    "funky_function(1)(2)(3)(4)()()()()"
]

for func_call in func_calls:
    try:
        ret_val = eval(func_call)
    except BaseException as exc:
        ret_val = exc

    # convert `ret_val` into a string 
    # and eliminate line-feeds, tabs, carriage-returns...
    ret_val = " ".join(str(ret_val).split())

    print(40*"-")
    print(func_call)
    print("return value".ljust(40), )
    print(40 * "-")
python-3.x 参数传递 装饰器 python-decorators

评论

1赞 Charles Duffy 9/18/2022
嘿。你听说过做“咖喱”的函数吗?这就是你在这里写的;在某些语言中,这是所有函数工作的标准方式。惹有什么意义?eval
0赞 Charles Duffy 9/18/2022
由于您只需要支持函数具有固定数量的参数的简单情况,因此无需运行时测试;您可以只检查函数以获取其参数计数,然后递归构造具有适当嵌套级别的函数。
0赞 Charles Duffy 9/18/2022
无论如何,知道“咖喱”这个名字应该对你自己解决问题有很大好处。f/e,它允许谷歌搜索,得出大量结果——f/e geeksforgeeks.org/currying-function-in-python;它并没有免费给你一个装饰器,但它告诉你你的装饰器应该做什么。
0赞 Charles Duffy 9/18/2022
此外,一个非常有用的咖喱工具是 functools.partial;使用它将使您的工作更轻松。:)
0赞 Charles Duffy 9/18/2022
(有关更多背景知识 - 我真的建议您阅读以更好地理解您的作业 - 请参阅咖喱和部分应用有什么区别?)

答:

0赞 Charles Duffy 9/19/2022 #1

作为一个不试图全面处理极端情况的快速尝试——

from inspect import signature
from functools import partial

def curry(orig_func):
    sig = signature(orig_func)
    target_arg_count = len(sig.parameters)
    args=[]
    new_func = orig_func
    for n in range(len(sig.parameters)):
        def curry_step(*args):
            if len(args) < target_arg_count:
                return partial(new_func, *args)
            return orig_func(*args)
        new_func = curry_step
    return new_func

@curry
def funky_function(x1, x2, x3, x4, /):
    return ", ".join(" ".join(str(x).split()) for x in [x1, x2, x3, x4])