Python装饰器函数包装器中的参数

时间:2020-05-23 01:46:51

标签: python python-decorators

对于装饰器来说是新手,这被认为是错误的代码吗?如果是这样,什么将是一个很好的替代品?

import functools
def error_handaler_decorator(func):
    @functools.wraps(func)
    def wrapper(error_message_for_wrapper = None, cont = True, *args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            if error_message_for_wraper != None:
                # Report error to user in application specific way
            if cont == True:
                return True

@error_handaler_decorator
def some_func(input_for_func):
    # Do a thing.

@error_handaler_decorator
def some_func_in_a_class(self,input):
    # Do another thing.

some_func(error_message_for_wrapper = something bad happened, input_for_func = some_input)

some_class.some_func_in_a_class(error_message_for_wrapper = something bad happened, cont = False, input_for_func = some_input)

这意味着我在调用装饰函数时必须传递包装变量,并且我认为我不能传递args,只能传递kwargs,但是它允许我定义错误消息是基于我传递给函数的信息,而不是基于我定义函数时的信息。

代码有效(至少与我测试过的一样),但是我的IDE(Visual Studio代码)非常生气,说:

方法调用中出现意外的关键字参数'error_message_for_wrapper'

我真的很想清理我的代码,我看到的替代方法是try: except:with:try: except:(至少在主观上)使我的代码混乱。

With.更好,但是我更希望将装饰器作为函数使用,对于项目来说效果更好。

我认为我不能将with作为函数。

2 个答案:

答案 0 :(得分:0)

好的,这将取决于您使用的Python版本。在python 3中,您可以执行以下操作:

public delegate RecursionRefFunc<T> RecursionRefFunc<T>(ref T arg);

public static RecursionRefFunc<T> Boo<T>(ref T input)
{
    Console.WriteLine(input); // Work in here
    return Boo;
}

public static void Main(string[] args)
{
    int x1 = 1, x2 = 2, x3 = 3, x4 = 4, x5 = 5;
    Boo(ref x1)(ref x2)(ref x3)(ref x4)(ref x5);
}

// Output: //
// 1
// 2
// 3
// 4
// 5

在python 2中(但在python 3中也可以使用),您可以使用:

def error_handler_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, error_message_for_wrapper = None, cont = True, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            if error_message_for_wrapper is not None:
                # Report error to user in application specific way
            if cont:
                return True
    return wrapper

答案 1 :(得分:0)

在这种情况下,您应该使用上下文管理器而不是装饰器。

from contextlib import contextmanager


@contextmanager
def handler(msg=None, cont=True):
    try:
        yield
    except Exception:
        if msg is not None:
            print(msg)
        if not cont:
            reraise


with handler("Don't divide by zero!"):
    3/0

print("OK")

将输出

Don't divide by zero!
OK

如果您在调用cont=False时设置了handler,则会看到Don't divide by zero,但是由于重新引发的异常而回溯会阻止打印OK


contextlib即将成为一个完整的圈子,它还提供了一种使用上下文管理器作为装饰器的方法。不过,您必须在没有contextmanager的帮助下定义上下文管理器。

from contextlib import ContextDecorator


class handler(ContextDecorator):
    def __init__(self, msg=None, cont=True):
        self.msg = msg
        self.cont = cont

    # ContextDecorator doesn't provide default definitions,
    # so we have to provide something, even it doesn't really
    # do anything.
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, tb):
        if exc_value is not None and self.msg is not None:
            print(self.msg)

        # Returning true suppresses any exception
        # that may have been raised in the context. Returning false
        # means the exception is raised as usual.
        return self.cont


# Scolds you, but returns None
@handler("Don't divide by zero")
def some_func(x):
    return 3/x

# Scolds you *and* raises the exception
@handler("Don't divide by zero", cont=False)
def some_other_func(x):
    return 3/x