不使用 类 的装饰器参数
Arguments to a decorator without using classes
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)
我的理解是:
每当 tracer
执行调用时,函数对象 rotate_list
作为参数传递。
我认为这不起作用的原因是因为跟踪器(以及与此相关的任何包装器)只接受可调用对象,enabled=False
不是可调用的所以它不起作用。
但是错误消息并不能很好地说明这一点,所以我想知道为什么错误消息是:
TypeError: tracer() missing 1 required positional argument: 'func'
我想括号内的参数首先被评估,这样就没有可调用对象被传递给跟踪器?
我想这可以通过使用 class 装饰器来解决,比如
class Trace:
def __init__(self, enabled=False):
print('Inside __init__')
self.enabled = enabled
然后 tracer = Trace(enabled=True)
会起作用,但我想看看如何在不使用 classes 的情况下解决这个问题。
============
编辑(解决方案):
不要介意,只需将其输入以确保我理解解决方案即可。
因为在装饰器中放置一个参数会使它像一个普通函数一样工作。解决方案是使该装饰器 return 成为另一个可调用对象(这是实际的装饰器)。
喜欢:
@dec
def foo: pass
会变成 foo = dec(foo)
@dec(ARG)
def foo: pass
会变成 foo = dec(ARG)(foo)
解决方案是使 dec
return 成为另一个可调用对象,它是实际的装饰器。例如,该函数将是 wrap
foo = dec(ARG)(foo)
将变为 foo = wrap(foo)
,其中 ARG
已传递给 dec
。
谢谢你们!我喜欢函数式编程。
要将函数以外的其他参数传递给装饰器函数,您需要嵌套多个 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
这个装饰器的作用与上面的装饰器完全相同。
编辑二
您可以用 类 做同样的事情,方法是将装饰器附加到初始化函数。
但这里有另一种方法,您可以通过将参数存储在 类:
中来制作带有参数的装饰器
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
只能用函数来完成。 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
这里所做的是创建 returns 装饰器的函数(跟踪器)。该装饰器接受一个函数。
如果您将它翻译成与 python 对任何装饰器所做的相同的格式,您就会明白为什么需要这样做。
每次python看到
@dec
def foo(): pass
它翻译成:
def foo(): pass
foo = dec(foo)
所以,当你有需要参数的装饰器时,它被翻译成这样:
foo = dec(ARGS)(foo)
因此,您需要做的是确保装饰器 returns 接受函数作为其参数。
PS: 装饰器使用functools.wraps来保留函数名、文档字符串等很好
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)
我的理解是:
每当 tracer
执行调用时,函数对象 rotate_list
作为参数传递。
我认为这不起作用的原因是因为跟踪器(以及与此相关的任何包装器)只接受可调用对象,enabled=False
不是可调用的所以它不起作用。
但是错误消息并不能很好地说明这一点,所以我想知道为什么错误消息是:
TypeError: tracer() missing 1 required positional argument: 'func'
我想括号内的参数首先被评估,这样就没有可调用对象被传递给跟踪器?
我想这可以通过使用 class 装饰器来解决,比如
class Trace:
def __init__(self, enabled=False):
print('Inside __init__')
self.enabled = enabled
然后 tracer = Trace(enabled=True)
会起作用,但我想看看如何在不使用 classes 的情况下解决这个问题。
============
编辑(解决方案): 不要介意,只需将其输入以确保我理解解决方案即可。 因为在装饰器中放置一个参数会使它像一个普通函数一样工作。解决方案是使该装饰器 return 成为另一个可调用对象(这是实际的装饰器)。
喜欢:
@dec
def foo: pass
会变成 foo = dec(foo)
@dec(ARG)
def foo: pass
会变成 foo = dec(ARG)(foo)
解决方案是使 dec
return 成为另一个可调用对象,它是实际的装饰器。例如,该函数将是 wrap
foo = dec(ARG)(foo)
将变为 foo = wrap(foo)
,其中 ARG
已传递给 dec
。
谢谢你们!我喜欢函数式编程。
要将函数以外的其他参数传递给装饰器函数,您需要嵌套多个 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
这个装饰器的作用与上面的装饰器完全相同。
编辑二
您可以用 类 做同样的事情,方法是将装饰器附加到初始化函数。
但这里有另一种方法,您可以通过将参数存储在 类:
中来制作带有参数的装饰器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
只能用函数来完成。 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
这里所做的是创建 returns 装饰器的函数(跟踪器)。该装饰器接受一个函数。
如果您将它翻译成与 python 对任何装饰器所做的相同的格式,您就会明白为什么需要这样做。
每次python看到
@dec
def foo(): pass
它翻译成:
def foo(): pass
foo = dec(foo)
所以,当你有需要参数的装饰器时,它被翻译成这样:
foo = dec(ARGS)(foo)
因此,您需要做的是确保装饰器 returns 接受函数作为其参数。
PS: 装饰器使用functools.wraps来保留函数名、文档字符串等很好