类装饰器与函数装饰器

29 投票
5 回答
7442 浏览
提问于 2025-04-16 21:24

在Python中,有两种方式来声明装饰器:

基于类的装饰器

class mydecorator(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        self.f(*k, **kw)
        # after f actions

基于函数的装饰器

def mydecorator(f):
    def decorator(*k, **kw):
        # before f actions
        f(*k, **kw)
        # after f actions

    return decorator          

这两种声明方式有什么区别吗?在什么情况下应该使用它们呢?

5 个回答

7

其实没有“两个方法”。只有一种方法(定义一个可调用的对象),或者说有很多种方法可以在Python中创建一个可调用的对象(它可以是其他对象的方法、一个lambda表达式的结果、一个“部分”对象,或者任何可以被调用的东西)。

定义函数是创建可调用对象最简单的方法,而且作为最简单的方法,在大多数情况下也是最好的。使用类可以让你有更多的选择来优雅地处理更复杂的情况(即使在最简单的情况下,看起来也很优雅),但它的作用可能不是那么明显。

21

当你想创建一个可以调用的东西,并且这个东西又能返回另一个可以调用的东西时,使用函数的方法会更简单、更省事。主要有两个不同之处:

  1. 使用函数的方法可以自动适用于类中的方法,而如果你用类的方法,就需要了解一些描述符的知识,还得定义一个 __get__ 方法。
  2. 类的方法更容易保持状态。你可以使用闭包,特别是在 Python 3 中,但一般来说,使用类的方法更适合保持状态。

此外,使用函数的方法可以在修改或存储后返回原始函数。

不过,装饰器可以返回的不仅仅是一个可调用的东西,或者可以返回一些更多的东西。使用类的话,你可以:

  1. 给被装饰的可调用对象添加方法和属性,或者对它们进行操作(这可就麻烦了)。
  2. 创建描述符,这些描述符在放到类中时会以特殊的方式工作(比如 classmethodproperty)。
  3. 使用继承来实现相似但又不同的装饰器。

如果你有疑问,可以问自己:你希望你的装饰器返回一个完全像函数那样工作的函数吗?那就用返回函数的方式。如果你希望你的装饰器返回一个自定义的对象,做一些不同于函数的事情,那就创建一个类并把它当作装饰器使用。

20

如果你想在装饰器中保持状态,最好使用一个类。

举个例子,这样做是行不通的:

def mydecorator(f):
    x = 0 
    def decorator():
        x += 1 # x is a nonlocal name and cant be modified
        return f(x)
    return decorator 

虽然有很多解决方法,但最简单的方式就是使用一个类。

class mydecorator(object):
    def __init__(self, f):
        self.f = f
        self.x = 0

    def __call__(self, *k, **kw):
        self.x += 1
        return f(self.x)

撰写回答