Python装饰器来转换函数或方法的输入和/或输出

2024-06-28 10:39:48 发布

您现在位置:Python中文网/ 问答频道 /正文

下面的功能确实(似乎)起作用了,但似乎有更多的锅炉板比必要的。在

我肯定还有更优雅的方式。它将把一些代码分解出来,这样看起来就不像现在的复制/粘贴/编辑补丁了。在

但请注意,优雅并不是一切:我不希望表演受到影响。例如,我可以通过做两个修饰符将代码减半:一个用于输入转换,另一个用于输出转换。但这将比目前的版本效率低。在

def input_output_decorator(preprocess=None, postprocess=None):
    def decorator(func):
        if inspect.ismethod(func):
            if preprocess is not None:
                if postprocess is not None:  # both pre and post processes
                    @wraps(func)
                    def func_wrapper(self, *args, **kwargs):
                        return postprocess(func(self, preprocess(*args, **kwargs)))
                else:  # a preprocess but no postprocess
                    @wraps(func)
                    def func_wrapper(self, *args, **kwargs):
                        return func(self, preprocess(*args, **kwargs))
            else:  # no preprocess
                if postprocess is not None:  # no preprocess, but a postprocess
                    @wraps(func)
                    def func_wrapper(self, *args, **kwargs):
                        return postprocess(func(*args, **kwargs))
                else:  # no pre or post process at all
                    func_wrapper = func
            return func_wrapper
        else:
            if preprocess is not None:
                if postprocess is not None:  # both pre and post processes
                    @wraps(func)
                    def func_wrapper(*args, **kwargs):
                        return postprocess(func(preprocess(*args, **kwargs)))
                else:  # a preprocess but no postprocess
                    @wraps(func)
                    def func_wrapper(*args, **kwargs):
                        return func(preprocess(*args, **kwargs))
            else:  # no preprocess
                if postprocess is not None:  # no preprocess, but a postprocess
                    @wraps(func)
                    def func_wrapper(*args, **kwargs):
                        return postprocess(func(*args, **kwargs))
                else:  # no pre or post process at all
                    func_wrapper = func
            return func_wrapper

    return decorator

一些使用示例:

^{pr2}$

我的最终实现基于@micky loo(已接受)的答案,并受到@a_guest答案的启发,是:


def input_output_decorator(preprocess=None, postprocess=None):
    def decorator(func):
        if preprocess and postprocess:
            def func_wrapper(*args, **kwargs):
                return postprocess(func(preprocess(*args, **kwargs)))
        elif preprocess:
            def func_wrapper(*args, **kwargs):
                return func(preprocess(*args, **kwargs))
        elif postprocess:
            def func_wrapper(*args, **kwargs):
                return postprocess(func(*args, **kwargs))
        else:  
            return func

        return wraps(func)(func_wrapper)

    return decorator

Tags: nononereturnifisdefargsdecorator
2条回答

你不需要做检查。if/else嵌套可以被展平以使代码更具可读性。在

from functools import wraps

def input_output_decorator(preprocess=None, postprocess=None):
    def decorator(func):
        if preprocess and postprocess:
            @wraps(func)
            def func_wrapper(*args, **kwargs):
                return postprocess(func(preprocess(*args, **kwargs)))
        elif preprocess:
            @wraps(func)
            def func_wrapper(*args, **kwargs):
                return func(preprocess(*args, **kwargs))
        elif postprocess:
            @wraps(func)
            def func_wrapper(*args, **kwargs):
                return postprocess(func(*args, **kwargs))
        else:
            func_wrapper = func

        return func_wrapper

    return decorator

使用两个不同的装饰器可以实现更紧凑的实现。的确,在前处理后处理的情况下,链中还会有一个函数调用,但这几乎不会影响性能。在

import functools
import inspect

def preprocess(pre):
    def decorator(func):
        if inspect.ismethod(func):
            def wrapper(self, *args, **kwargs):
                return func(self, pre(*args, **kwargs))
        else:
            def wrapper(*args, **kwargs):
                return func(pre(*args, **kwargs))
        return functools.wraps(func)(wrapper)
    return decorator

def postprocess(post):
    def decorator(func):
        def wrapper(*args, **kwargs):
            return post(func(*args, **kwargs))
        return functools.wraps(func)(wrapper)
    return decorator

相关问题 更多 >