简体   繁体   English

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

[英]parameters in Python decorator function wrapper

Fairly new to decorators, is this considered bad code?对装饰器来说相当新,这被认为是糟糕的代码吗? If it is, what would be a good substitute?如果是,什么是好的替代品?

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)

This means that I have to pass the wrapper variables when I call the decorated function, and I don't think I can pass args , only kwargs , but it allows me to define the error message based on what I pass to the function, not when I define the function.这意味着当我调用修饰的 function 时,我必须传递包装器变量,而且我认为我不能传递args ,只能传递kwargs ,但它允许我根据传递给 function 的内容来定义错误消息,而不是当我定义 function 时。

The code works, (at least as much as I have tested it), but my IDE (Visual Studio code) gets very angry, saying:该代码有效,(至少与我测试过的一样多),但我的 IDE(Visual Studio 代码)非常生气,说:

Unexpected keyword argument 'error_message_for_wrapper' in method call方法调用中出现意外的关键字参数“error_message_for_wrapper”

I really want to clean up my code, and the alternatives I see are try: except: or with: .我真的很想清理我的代码,我看到的替代方法是try: except:with: try: except: makes my code messy, (at least subjectively). try: except:使我的代码混乱,(至少主观上)。

With. is better, but I would rather have my decorators as functions, it works better for the project.更好,但我宁愿将我的装饰器作为函数,它对项目更有效。

I don't think I can have with as a function.我不认为我可以with function。

Okay this is going to depend on which Python version you use I believe.好的,这将取决于我相信您使用的 Python 版本。 In python 3 you can just do:在 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

In python 2 (but would also work in python 3) you can use:在 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

This is probably a case where you should use a context manager rather than a decorator.这可能是您应该使用上下文管理器而不是装饰器的情况。

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")

will output将 output

Don't divide by zero!
OK

If you set cont=False when you call handler , you'll see Don't divide by zero , but then a traceback as the re-raised exception prevents OK from being printed.如果您在调用handler时设置cont=False ,您将看到Don't divide by zero ,但随后作为重新引发的异常的回溯会阻止打印OK


Coming full circle, contextlib also provides a way to use a context manager as a decorator.循环往复, contextlib还提供了一种将上下文管理器用作装饰器的方法。 You'll have to define the context manager without the help of contextmanager , though.但是,您必须在没有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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM