Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 带参数的装饰器:无参数时避免使用括号_Python_Logging_Python Decorators - Fatal编程技术网

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

Python 带参数的装饰器:无参数时避免使用括号,python,logging,python-decorators,Python,Logging,Python Decorators,下面是我的@logged()装饰器生成器。下面是它的大致工作原理: 它接受记录器实例和禁用标志 如果disabled为False,则在修饰函数之前/之后输出一些日志 如果禁用为真,则不会输出任何内容,也会抑制已修饰功能的记录器 logger和disabled参数都有其默认值。但是,当我想使用默认值时,我仍然必须写空括号,如下所示: @logged() def foo(): pass 当我只需要默认参数时,有没有办法去掉这些空括号?下面是我想要的一个例子: @logged def foo

下面是我的
@logged()
装饰器生成器。下面是它的大致工作原理:

  • 它接受
    记录器
    实例和
    禁用
    标志
  • 如果
    disabled
    False
    ,则在修饰函数之前/之后输出一些日志
  • 如果
    禁用
    ,则不会输出任何内容,也会抑制已修饰功能的
    记录器
  • logger
    disabled
    参数都有其默认值。但是,当我想使用默认值时,我仍然必须写空括号,如下所示:

    @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()
    

    您可以在装饰器主体内使用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,表示最后两个参数为仅关键字参数,因为func旁边的任何其他参数都被解包到
    *
    中,在这种情况下没有标识符,因此实际上是“丢失的”。这意味着您在正常使用装饰器时必须使用关键字参数:

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

    请看这里:


    我对此非常恼火,最终写了一个库来解决这个问题:

    它支持两种开发样式:嵌套的(与python装饰器工厂中一样)和扁平的(少一层嵌套)。以下是您的示例在展开模式下的实现方式:

    from decopatch import function_decorator, DECORATED
    from makefun import wraps
    
    @function_decorator
    def logged(disabled=False, logger=logging.getLogger('default'), func=DECORATED):
    
        # (1) create a signature-preserving wrapper
        @wraps(func)
        def _func_wrapper(*f_args, **f_kwargs):
            # stuff
            result = func(*f_args, **f_kwargs)
            # stuff
            return result
    
        # (2) return it
        return _func_wrapper
    
    注意,我在这里使用而不是
    functools.wrapps
    ,这样签名就被完全保留了(如果参数无效,就根本不调用包装器)

    decopatch
    支持一种额外的开发风格,我称之为double flat,它专门用于创建像这样的保留签名的函数包装器。您的示例的实现方式如下:

    from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS
    
    @function_decorator
    def logged(disabled=False, logger=logging.getLogger('default'), 
               func=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
        # this is directly the signature-preserving wrapper
        # stuff
        result = func(*f_args, **f_kwargs)
        # stuff
        return result
    
    您可以检查这两种样式是否按预期工作:

    @logged(disabled=True)
    def foo():
        pass
    
    @logged
    def bar():
        pass
    
    foo()
    
    bar()
    
    请查看以了解详细信息。

    或使用分部(可在Python cookbook:9.6中找到解决方案)

    @logged(disabled=True)
    def foo():
        pass
    
    @logged
    def bar():
        pass
    
    foo()
    
    bar()
    
    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