带参数的装饰器:没有参数时避免使用括号

ali*_*noi 2 python logging python-decorators

下面是我的@logged()装饰器制造商。它的工作原理大致如下:

  1. 它接受一个logger实例和一个disabled标志。
  2. 如果disabledFalse,则在装饰函数之前/之后输出一些日志。
  3. 如果disabledTrue,则它不输出任何内容并抑制logger装饰函数的 。

无论是loggerdisabled参数有自己的缺省值。但是,当我想使用默认值时,我仍然需要写空括号,如下所示:

@logged()
def foo():
    pass
Run Code Online (Sandbox Code Playgroud)

当我只想要默认参数时,有没有办法摆脱这些空括号?这是我想要的一个例子:

@logged
def foo():
    pass

@logged(disabled=True)
def bar():
    pass
Run Code Online (Sandbox Code Playgroud)

@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()
Run Code Online (Sandbox Code Playgroud)

N C*_*han 5

您可以在装饰器主体内使用 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
Run Code Online (Sandbox Code Playgroud)

(func=None, *, logger=..., disabled=False)如FUNC旁任何多个参数是解压缩到具有一个星号ARG来表示的最后2个参数作为唯一关键字参数*其具有在此情况下,没有识别符等等都是有效的“丢失”。这意味着您在正常使用装饰器时必须使用关键字参数:

@logged(
    disabled=True,
    logged=logging.logger # ...
)
def foo(): pass
Run Code Online (Sandbox Code Playgroud)

或者...

@logged
def bar(): pass
Run Code Online (Sandbox Code Playgroud)

请参阅此处:如何使用可选参数构建装饰器?