如何将装饰器包装在另一个 类 方法周围?
How to wrap a decorator around another classes method?
我创建了一个用于管理日志记录的装饰器。我希望在装饰函数运行之前和之后进行日志记录。该函数在与非常基本的函数交互时工作正常,但是,当与其他 类 的一部分的方法交互时,事情就会中断。我怀疑问题是因为有 2 个 self
个参数。您知道如何解决吗?
简化装饰器Class
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_type = logging_type
def __call__(self, decorated_function:callable):
self.func = decorated_function
return getattr(self, self.logging_type)
def debug(self, *args, **kwargs):
print("starting function")
output = self.func(*args, **kwargs)
print("Completing Function")
return output
我们看到装饰器作用于基本功能:
@Logger(logging_type="debug")
def simple_function(x):
return x**2
In [2]: simple_function(3)
starting function
Completing Function
Out[2]: 9
但是,与其他人一起工作时失败 类:
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
In [16]: test = BigClass()
...: test.cool_function(3)
starting function
然后在输出行遇到类型错误:
TypeError: cool_function() missing 1 required positional argument: 'input1'
想法?
问题是您正在使用绑定方法类型装饰您的函数,查看 type(BigClass.cool_function)
,您会看到类似于:<bound method Logger.debug of <__main__.Logger object at 0x11081f7c0>
的内容。由于绑定方法对象不是函数,它们不实现描述符协议以将实例绑定为第一个参数,因此,实例永远不会作为第一个参数隐式传递。
最好的解决方案是避免使用基于 class 的装饰器。以下是您如何使用基于函数的装饰器实现您正在做的事情,使用闭包来维护内部状态:
from functools import wraps
def logger(*, logging_type): # I prefer keyword-only arugments for decorators, but that is your call...
def decorator(func):
@wraps(func)
def debug(*args, **kwargs):
print("starting function")
result = func(*args, **kwargs)
print("ending function")
return result
@wraps(func)
def another_option(*args, **kwargs):
print("another option")
return func(*args, **kwargs)
options = {"debug": debug, "another_option": another_option}
return options[logging_type]
return decorator
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
@logger(logging_type="another_option")
def another_function(self):
return self.stuff*100
务必阅读 juanpa.arrivillaga 的信息性回答。但这里有一个更简单的方法。在编写一个 class 这种类型的装饰器时, __call__
应该 return 一个普通函数而不是一个成员函数,像这样:
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_function = getattr(self, logging_type)
def __call__(self, decorated_function: callable):
def f(*args, **kwargs):
return self.logging_function(decorated_function, *args, **kwargs)
return f
def debug(self, decorated_function, *args, **kwargs):
print("starting function")
output = decorated_function(*args, **kwargs)
print("Completing Function")
return output
@Logger(logging_type="debug")
def simple_function(x):
return x**2
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
print(simple_function(12))
test = BigClass()
print(test.cool_function(3))
输出:
starting function
Completing Function
144
starting function
Completing Function
13
我创建了一个用于管理日志记录的装饰器。我希望在装饰函数运行之前和之后进行日志记录。该函数在与非常基本的函数交互时工作正常,但是,当与其他 类 的一部分的方法交互时,事情就会中断。我怀疑问题是因为有 2 个 self
个参数。您知道如何解决吗?
简化装饰器Class
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_type = logging_type
def __call__(self, decorated_function:callable):
self.func = decorated_function
return getattr(self, self.logging_type)
def debug(self, *args, **kwargs):
print("starting function")
output = self.func(*args, **kwargs)
print("Completing Function")
return output
我们看到装饰器作用于基本功能:
@Logger(logging_type="debug")
def simple_function(x):
return x**2
In [2]: simple_function(3)
starting function
Completing Function
Out[2]: 9
但是,与其他人一起工作时失败 类:
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
In [16]: test = BigClass()
...: test.cool_function(3)
starting function
然后在输出行遇到类型错误:
TypeError: cool_function() missing 1 required positional argument: 'input1'
想法?
问题是您正在使用绑定方法类型装饰您的函数,查看 type(BigClass.cool_function)
,您会看到类似于:<bound method Logger.debug of <__main__.Logger object at 0x11081f7c0>
的内容。由于绑定方法对象不是函数,它们不实现描述符协议以将实例绑定为第一个参数,因此,实例永远不会作为第一个参数隐式传递。
最好的解决方案是避免使用基于 class 的装饰器。以下是您如何使用基于函数的装饰器实现您正在做的事情,使用闭包来维护内部状态:
from functools import wraps
def logger(*, logging_type): # I prefer keyword-only arugments for decorators, but that is your call...
def decorator(func):
@wraps(func)
def debug(*args, **kwargs):
print("starting function")
result = func(*args, **kwargs)
print("ending function")
return result
@wraps(func)
def another_option(*args, **kwargs):
print("another option")
return func(*args, **kwargs)
options = {"debug": debug, "another_option": another_option}
return options[logging_type]
return decorator
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
@logger(logging_type="another_option")
def another_function(self):
return self.stuff*100
务必阅读 juanpa.arrivillaga 的信息性回答。但这里有一个更简单的方法。在编写一个 class 这种类型的装饰器时, __call__
应该 return 一个普通函数而不是一个成员函数,像这样:
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_function = getattr(self, logging_type)
def __call__(self, decorated_function: callable):
def f(*args, **kwargs):
return self.logging_function(decorated_function, *args, **kwargs)
return f
def debug(self, decorated_function, *args, **kwargs):
print("starting function")
output = decorated_function(*args, **kwargs)
print("Completing Function")
return output
@Logger(logging_type="debug")
def simple_function(x):
return x**2
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
@Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
print(simple_function(12))
test = BigClass()
print(test.cool_function(3))
输出:
starting function
Completing Function
144
starting function
Completing Function
13