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