在Python中,是否可以使用相同的装饰器来装饰类和非类方法?

Mit*_*lad 3 python closures arguments decorator wrapper

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

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

   return wrapper
Run Code Online (Sandbox Code Playgroud)

但是,如果我想装饰一个类方法,我必须修改wrapper()以获取'self',否则我会收到以下错误:

TypeError: wrapper() takes no arguments (1 given)
Run Code Online (Sandbox Code Playgroud)

当然,在那一点上我不能用它来装饰任何非类方法,因为这样就会发生这样的错误:

TypeError: wrapper() takes exactly 1 argument (0 given)
Run Code Online (Sandbox Code Playgroud)

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

Bre*_*arn 7

在平常的事情是如此它接受定义您的包装*args和**kwargs并将它们传递给它包装的功能.这样它就可以包装任何函数.

另外,我得到的印象是你所谓的"类方法"是Python所谓的"实例方法",而你所谓的"非类方法"就是Python所谓的"函数".Python中的"非类方法"(例如,实例方法)采用self参数.


Tad*_*eck 5

实例方法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
Run Code Online (Sandbox Code Playgroud)

证明(或者更容易说明的例子)在这里:

>>> t = Test()
>>> t.standard_method()
((<__main__.Test object at 0x0000000002B47CC0>,), {})
>>> t.class_method()
((<class '__main__.Test'>,), {})
>>> t.static_method()
((), {})
Run Code Online (Sandbox Code Playgroud)

如您所见,传递的参数列表根据您选择的方法类型而有所不同。您面临的问题是可变数量的参数。

解

有一个解决方案-使用参数解包:

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
Run Code Online (Sandbox Code Playgroud)

之后,by返回的函数some_decorator将接受与修饰函数相同数量的参数。

因此,这两个示例都将起作用:

@some_decorator
def say_hello():
    print 'hello'

@some_decorator
def say_something(something):
    print something
Run Code Online (Sandbox Code Playgroud)

附录

为了给您提供完整的示例,最好使用这样的构造(请注意的用法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
Run Code Online (Sandbox Code Playgroud)

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