def tracer(func, enabled=True):
def wrap(*args, **kwargs):
if enabled:
print('Calling {}'.format(func))
return func(*args, **kwargs)
return wrap
@tracer(enabled=False)
def rotate_list(l):
return l[1:] + [l[0]]
我对于为什么这不起作用有点困惑,特别是这一部分:@tracer(enabled=False)
我所理解的是:
只要rotate_list
执行调用,函数对象tracer
就会作为参数传递。
我认为这不起作用的原因是因为跟踪器(以及任何包装器)只接受可调用对象,enabled=False
不可调用,因此它不起作用。
然而,错误消息并不能说明这一点,所以我想知道错误消息的原因是:
TypeError: tracer() missing 1 required positional argument: 'func'
我想首先评估括号内的参数,以便没有可调用对象传递给跟踪器?
我想这可以通过使用类装饰器来解决,例如
class Trace:
def __init__(self, enabled=False):
print('Inside __init__')
self.enabled = enabled
然后tracer = Trace(enabled=True)
会起作用,但我想看看如何在不使用类的情况下解决这个问题。
============
编辑(解决方案): 不要介意这一点,只需输入它以确保我理解解决方案。 因为在装饰器中放置一个参数使它就像一个普通的函数。解决方案是让装饰器返回另一个可调用对象(这是实际的装饰器)。
像:
@dec
def foo: pass
将成为foo = dec(foo)
@dec(ARG)
def foo: pass
将成为foo = dec(ARG)(foo)
解决方案是让dec
返回另一个可调用的,它是实际的装饰器。例如,该函数将是wrap
foo = dec(ARG)(foo)
将成为foo = wrap(foo)
,其中ARG
已传递给dec
。
多谢你们!我喜欢函数式编程。
答案 0 :(得分:1)
要将除函数之外的其他参数传递给装饰器函数,您可以嵌套多个defs:
def my_decorator(flagDoThat)
def internal(func):
def wrapper(*argv, **kwv):
retval = func(*argv, **kwv)
if flagDoThat:
print("Hello", retval)
return retval
wrapper.__name__ = func.__name__ #update name
return wrapper
return internal
@my_decorator(True)
def my_func(): return "world"
#equals to
tmp = my_decorator(True)
@tmp
def my_func(): return "world"
这个装饰器有助于构建其他装饰器。虽然这包含多个嵌套函数,但它允许您定义只有两个图层和参数的装饰器,就像您一样:
def decorator(keepFunctionName=True):
def internal(func):
def newFunc(*argv, **kwv):
def decoWrapper(theFuncUsedInFunc):
fRet = func(theFuncUsedInFunc, *argv, **kwv)
if keepFunctionName:
fRet.__name__ = theFuncUsedInFunc.__name__
return fRet
return decoWrapper
return newFunc
return internal
可以像这样使用:
@decorator()
def my_decorator(func, flagDoThat):
def wrapper(*argv, **kwv):
retval = func(*argv, **kwv)
if flagDoThat:
print("Hello", retval)
return retval
return wrapper
这个装饰器完全按照上面的装饰器做的。
通过将装饰器附加到init函数,您可以对类执行相同的操作。
但是这是另一种方法,你可以通过将它们存储在类中来设置带有参数的装饰器:
class my_decorator:
__slots__ = ["flagDoThat"] # optional line
def __init__(self, flagDoThat):
self.flagDoThat = flagDoThat
def __call__(self, func):
def wrapper(*argv, **kwv):
retval = func(*argv, **kwv)
if self.flagDoThat:
print("Hello", retval)
return retval
return wrapper
答案 1 :(得分:1)
只能用功能来完成。 tracer
应如下所示:
def tracer(enabled=False):
def wrapper(func):
def wrap(*args, **kwargs):
if enabled:
print('Calling {}'.format(func))
return func(*args, **kwargs)
return wrap
return wrapper
这里做的是你创建了返回装饰器的函数(tracer)。该装饰器接受一个功能。
如果你把它翻译成python对任何装饰者的格式,你会明白为什么这是必需的。
每次python看到
@dec
def foo(): pass
它将其翻译为:
def foo(): pass
foo = dec(foo)
所以,当你有需要参数的装饰器时,它就像这样翻译:
foo = dec(ARGS)(foo)
所以,你需要做的是确保装饰器返回一个接受函数作为参数的东西。
PS:使用functools.wraps装饰器来保存函数名,文档字符串等很好。