【问题标题】:parameters in Python decorator function wrapperPython 装饰器函数包装器中的参数
【发布时间】:2020-05-23 01:46:51
【问题描述】:

对于装饰器来说还很陌生,这会被认为是糟糕的代码吗?如果是,什么是好的替代品?

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 作为函数。

【问题讨论】:

  • VSCode 正在做静态分析;它不会跟踪代码的(未来)执行以发现some_func,它被静态绑定到具有一个参数的函数,动态地反弹到一个完全不同的签名函数。

标签: python python-decorators


【解决方案1】:

好的,这取决于我相信您使用的 Python 版本。在 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

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

def error_handler_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        error_message_for_wrapper = kwargs.pop('error_message_for_wrapper', None)
        cont = kwargs.pop('cont', False)
        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

【讨论】:

    【解决方案2】:

    这可能是您应该使用上下文管理器而不是装饰器的情况。

    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
    

    如果您在调用handler 时设置cont=False,您将看到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
    

    【讨论】:

    • 谢谢!在这种情况下,我必须用@contextmanager 装饰处理程序,是吗?我可能过度消毒了。问题是我希望程序能够以不同的方式从不同的来源获取输入,因此很难判断哪些内容已被清理。我会考虑是否可以重构以通过验证阻塞点发送所有输入流。 Ps:抱歉多次编辑,不知道如何在手机上换行。
    • 对不起,是的,我导入了但忘记使用了。
    猜你喜欢
    • 2011-06-25
    • 2015-09-03
    • 2014-07-21
    • 2018-10-03
    • 1970-01-01
    • 2010-10-14
    • 2018-03-07
    • 2013-09-18
    • 2015-10-09
    相关资源
    最近更新 更多