如何使用装饰器按名称替换参数?

时间:2018-04-23 18:24:55

标签: python python-decorators

如何创建一个装饰器来替换传递给装饰函数的参数值?

from functools import wraps

def transform_param(param_to_transform):
    def decorator(func):
        @wraps(func)
        def with_transform_param(*args, **kwargs):
            import inspect

            signature = inspect.siganture(func)

            # if param_to_transform in signature
            # transform that parameter in some way
            # and pass to func

            return func(*args, **kwargs)

        return with_transform_param

    return decorator

我试图创建一个装饰器来自动解密传递给函数的参数,这样装饰的函数就会与解密的问题分开。

对于内心部分,我能想到的最好的是:

if parameter == param_to_transform:
    args = (
        args[:index]
        + (transform(args[index]),)
        + args[index+1:]
    )

但这与kwargs打破了。

1 个答案:

答案 0 :(得分:1)

装饰器的想法很好,但是因为这理论上一直适用于任何旧函数的命名参数,所以你可以更简单地实现同样的目的。

def some_function(a=3, b=4):
    return a + b

def my_transform(**kwargs):
    # Pretend the decryption transform is just subtracting 10.
    return {k: v - 10 for k, v in kwargs.items()}

def decrypt_apply(transform, f, *args, **kwargs):
    return f(*args, **transform(**kwargs))

例如:

In [30]: decrypt_apply(my_transform, some_function, a=24, b=38)
Out[30]: 42

这类似于易于构成变换函数和基函数,尤其是例如。使用functools.partial,它不涉及需要反省参数签名或涉及装饰器的误导。

另一件好事是,如果你将错误命名的关键字参数传递给decrypt_apply函数,它会很乐意尝试将它们传递给变换器和基函数,这会产生很好的错误,表明调用不受支持参数,而不是可能在装饰器的主体中默默地允许它。

如果要扩展它以转换位置或关键字参数,只需传入两个不同的转换函数(每个转换函数都可以进一步调用其他转换函数以允许重复使用大量代码)。

例如:

def some_function(a, b=3, c=4):
    return a + b + c

def my_logic(value):
    # Pretend the decryption transform is just subtracting 10.
    return value - 10

def arg_transform(logic, *args):
    return tuple(logic(arg) for arg in args)

def kwarg_transform(logic, **kwargs):
    return {k: logic(v) for k, v in kwargs.items()}

def decrypt_apply(logic, f, *args, **kwargs):
    # This function could parameterize `logic` as I did here,
    # or it could directly parameterize `arg_transform` and
    # `kwarg_transform`, just depends on which better factors
    # your specific business logic. 
    return f(*arg_transform(logic, *args), **kwarg_transform(logic, **kwargs))

E.g:

In [37]: decrypt_apply(my_logic, some_function, 24, b=10, c=38)
Out[37]: 42

显然,您可以将logic参数arg_transformkwarg_transform复杂化,检查特定变量名称,根据输入的不同位置应用不同的变换,等。

它很快就会变得很乱,这就是为什么通常最好不要尝试一堆棘手的元编程,而只是编写一个简单的辅助模块,它直接为你实际拥有的情况提供变换......不要担心扩展它来处理更通用的情况或更具有参数的变换器逻辑的一般概念,直到你不再通过仅添加辅助函数来实现它。

我认为故事的寓意是,对于简单的情况,装饰器只不过是一流的函数处理程序。但是由于Python直接提供了一流的函数,你可以编写操作其他函数的函数。

组织它作为装饰器IMO的好处来自于装饰逻辑更复杂并且不是参数的简单转换,或者当存在一些其他约束导致装饰器更友好时,例如API