我有一个简单的异常日志记录装饰器,当我的脚本抛出异常时,它可以方便地发送自己的电子邮件。
def logExceptions(func):
def wrapper():
try:
func()
except Exception, e:
logger.exception(e)
return wrapper
但是,如果我想装饰一个类方法,我必须修改wrapper()以获取'self',否则我会收到以下错误:
TypeError: wrapper() takes no arguments (1 given)
当然,在那一点上我不能用它来装饰任何非类方法,因为这样就会出现这个错误:
TypeError: wrapper() takes exactly 1 argument (0 given)
有解决这个问题的干净方法吗?谢谢=)
答案 0 :(得分:7)
通常的做法是定义你的包装器,使它接受*args
和**kwargs
并将它们传递给它包装的函数。这样它就可以包装任何函数。
另外,我得到的印象是,你所谓的“类方法”是Python所谓的“实例方法”,而你所谓的“非类方法”就是Python所谓的“函数”。 Python中的“非类方法”(例如,实例方法)采用self
参数。
答案 1 :(得分:4)
classmethod
和staticmethod
首先注意:静态方法和类方法都是函数,因此标准函数规则主要适用于它们。我理解你的问题是关于静态方法(没有传递额外参数)和类方法(它在第一个参数中接收类)之间的区别:
class Test(object):
def standard_method(*args, **kwargs):
# it is instance method (first argument will be instance)
return args, kwargs
@classmethod
def class_method(*args, **kwargs):
# it is class method (first argument will be class)
return args, kwargs
@staticmethod
def static_method(*args, **kwargs):
# it is static method (receives only arguments passed explicitly)
return args, kwargs
证明(或者说不言自明的例子)在这里:
>>> t = Test()
>>> t.standard_method()
((<__main__.Test object at 0x0000000002B47CC0>,), {})
>>> t.class_method()
((<class '__main__.Test'>,), {})
>>> t.static_method()
((), {})
如您所见,传递的参数列表根据您选择的方法类型而有所不同。您面临的问题是可变数量的参数。
有一个解决方案 - 使用参数解包:
def some_decorator(func):
def wrapper(*args, **kwargs):
# do something here
# args is a tuple with positional args, kwargs is dict with keyword args
return func(*args, **kwargs)
return wrapper
之后,some_decorator
返回的函数将接受与装饰函数相同数量的参数。
所以这两个例子都有效:
@some_decorator
def say_hello():
print 'hello'
@some_decorator
def say_something(something):
print something
为了给你完整的例子,如果你使用这样的结构会很好(注意functools.wraps
的用法):
from functools import wraps
def some_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# do something here
# args is a tuple with positional args, kwargs is dict with keyword args
return func(*args, **kwargs)
return wrapper
原因在documentation for functools.wraps()
中列出:它保留了函数名和docstring,有效地使包装器看起来像包装函数(有时候很有用)。
答案 2 :(得分:2)
装饰的替代方法是使用sys.excepthook
,它是一个回调函数,对所有未被捕获的异常进行操作,您可以为其分配自定义日志记录功能。好处是,您不需要毁掉(更重要的是,跟踪)您有兴趣记录异常的每个函数。