3

我有一个简单的异常记录装饰器,当我的脚本抛出异常时,它可以方便地给自己发送电子邮件。

def logExceptions(func):
   def wrapper():
      try:
         func()
      except Exception, e:
         logger.exception(e)

   return wrapper

但是,如果我想装饰一个类方法,我必须修改 wrapper() 以获取“自我”,否则会出现以下错误:

TypeError: wrapper() takes no arguments (1 given)

当然,此时我不能用它来装饰任何非类方法,因为这样就会出现这个错误:

TypeError: wrapper() takes exactly 1 argument (0 given)

有没有一种干净的方法来解决这个问题?谢谢你=)

4

3 回答 3

7

通常的做法是定义您的包装器,以便它接受*args并将**kwargs它们传递给它包装的函数。这样它可以包装任何功能。

另外,我的印象是,您所说的“类方法”是 Python 所说的“实例方法”,而您所说的“非类方法”是 Python 所说的“函数”。Python 中的“非类方法”(例如,实例方法)需要一个self参数。

于 2012-09-11T03:01:35.243 回答
5

实例方法classmethodstaticmethod

首先要注意:静态方法和类方法也是函数,因此标准函数规则大多适用于它们。我了解您的问题是关于静态方法(没有传递额外的参数)和类方法(在第一个参数中接收类)之间的区别:

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

其原因在文档functools.wraps()中列出:它保留了函数名称和文档字符串,有效地导致包装器看起来像一个包装的函数(有时很有用)。

于 2012-09-11T03:08:22.273 回答
2

装饰的替代方法是使用sys.excepthook,它是对所有未捕获的异常进行操作的回调,您可以为其分配自定义日志记录函数。这样做的好处是,您不需要破坏(更重要的是,跟踪)您对记录异常感兴趣的每个函数。

于 2012-09-11T03:08:10.127 回答