带参数的装饰器:无参数时避免括号

时间:2018-09-01 07:48:32

标签: python logging python-decorators

以下是我的@logged()装饰制造商。大致是这样的:

  1. 它接受一个logger实例和一个disabled标志。
  2. 如果disabledFalse,则会在修饰功能之前/之后输出一些日志。
  3. 如果disabledTrue,则它不输出任何内容,并且不显示装饰功能的logger

loggerdisabled参数都有其默认值。但是,当我想使用默认值时,仍然需要写空括号,如下所示:

@logged()
def foo():
    pass

当我只想要默认参数时,有什么方法可以消除这些空括号?这是我想要的示例:

@logged
def foo():
    pass

@logged(disabled=True)
def bar():
    pass

@logged()装饰器制造商的代码:

import logging
import logging.config

from functools import wraps

def logged(logger=logging.getLogger('default'), disabled=False):
    '''
    Create a configured decorator that controls logging output of a function

    :param logger: the logger to send output to
    :param disabled: True if the logger should be disabled, False otherwise
    '''

    def logged_decorator(foo):
        '''
        Decorate a function and surround its call with enter/leave logs

        Produce logging output of the form:
        > enter foo
          ...
        > leave foo (returned value)
        '''

        @wraps(foo)
        def wrapper(*args, **kwargs):

            was_disabled = logger.disabled

            # If the logger was not already disabled by something else, see if
            # it should be disabled by us. Important effect: if foo uses the
            # same logger, then any inner logging will be disabled as well.
            if not was_disabled:
                logger.disabled = disabled

            logger.debug(f'enter {foo.__qualname__}')

            result = foo(*args, **kwargs)

            logger.debug(f'leave {foo.__qualname__} ({result})')

            # Restore previous logger state:
            logger.disabled = was_disabled

            return result

        return wrapper

    return logged_decorator

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'verbose': {
            'format': '%(asctime)22s %(levelname)7s %(module)10s %(process)6d %(thread)15d %(message)s'
        }
        , 'simple': {
            'format': '%(levelname)s %(message)s'
        }
    }
    , 'handlers': {
        'console': {
            'level': 'DEBUG'
            , 'class': 'logging.StreamHandler'
            , 'formatter': 'verbose'
        }
    },
    'loggers': {
        'default': {
            'handlers': ['console']
            , 'level': 'DEBUG',
        }
    }
})

@logged()
def foo():
    pass

if __name__ == '__main__':
    foo()

2 个答案:

答案 0 :(得分:4)

您可以在装饰器主体内使用if-else:

def logged(func=None, *, disabled=False, logger=logging.default()):
    def logged_decorator(func):
        # stuff
        def wrapper(*args_, **kwargs):
            # stuff
            result = func(*args_, **kwargs)
            # stuff 
            return result
        return wrapper
    if func:
        return logged_decorator(func)
    else:
        return logged_decorator

(func=None, *, logger=..., disabled=False)有一个星号arg,表示最后2个参数为仅关键字的参数,因为func旁边的其他任何参数都被解压缩到*中,在这种情况下,该标识符没有标识符,因此实际上是'丢失'。这意味着您通常在使用装饰器时必须 使用关键字参数:

@logged(
    disabled=True,
    logged=logging.logger # ...
)
def foo(): pass

或者...

@logged
def bar(): pass
  

参见此处:How to build a decorator with optional parameters?

答案 1 :(得分:1)

或使用部分(在Python食谱中找到的解决方案:9.6)

from functools import wraps, partial

def foo(func=None, *, a=None, b=None):
    if func is None:
        return partial(foo, a=a, b=b)

    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper