如何使用装饰器拦截和预处理参数?

How to intercept and preproccess arguments with decorators?

我有一种情况,我正在编写许多形式的函数

def func(arg):
    arg=pre_process(arg)
    return do_work(arg)

我希望能够以更简单的方式对 arg 进行预处理。我尝试使用如下示例中的装饰器


from typing import Callable


def super_decorator(arg_name: str, pre_fn: Callable) -> Callable:
    def decorator(fn):
        def inner(*args, **kwargs):
            assert arg_name in kwargs.keys()
            kwargs[arg_name] = pre_fn(arg_name)
            return fn(*args, **kwargs)

        return inner

    return decorator



#example

def pre_processor(x):
    return "pre-processed"


@super_decorator("arg1", pre_processor)
def func(*, arg1=None):
    return arg1


print(func(arg1="hello world"))

使用这种技术我得到了正确的输出

pre-processed

不过这有点老套,我不得不强制使用关键字参数。有没有更好的方法?

Is there a better way?

不,我不这么认为。

代码审查时间。好的,在查看了您的代码后,我竖起大拇指。 LGTM。发货!

对不起。我明白为什么你会觉得有很多仪式感 继续在这段代码中,但你写的是表达它的最自然的方式。 感谢注释和使用 typing.

您编写的装饰器比您的第一个示例所需的要通用得多。你可以简单地写

def compose(preprocessor):
    def decorator(f):
        def inner(arg):
            return f(preprocessor(arg))
        return inner
    return decorator


@compose(pre_processor)
def func(arg):
    return do_work(arg)

因此,为了确定您的 super_decorator 是否设计良好,您需要更准确地定义它试图解决的问题。


我使用名称 compose 是因为您的超级装饰器只是组合运算符的柯里化形式。如果你知道 Haskell,你只需写

func = do_work . pre_processor

我见过的一种模式是使用对函数的引用作为您希望将这些函数应用到的参数的注释

from typing import Callable
from functools import wraps
from inspect import signature
from collections import OrderedDict


def use_annotations(func):
    sig = signature(func)
    @wraps(func)
    def inner(*args, **kwargs):
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        bound.arguments = OrderedDict(
            (param, sig.parameters[param].annotation(value))
            if isinstance(sig.parameters[param].annotation, Callable)
            else (param, value)
            for param, value in bound.arguments.items()
        )
        return func(*bound.args, **bound.kwargs)
    return inner


def pre_processor(x):
    return "pre-processed"


@use_annotations
def func(*, arg1: pre_processor=None):
    return arg1


print(func(arg1="hello world"))

对于大多数希望您使用注释进行类型提示的工具,这可能不会很好地发挥作用。