深入理解Python装饰器--@

什么是装饰器

装饰器可以对已经存在的对象添加额外的功能,而不需要修改它原本的代码,使代码看起来更简洁。Python本身提供了许多带有具有特定角色的内置装饰器:静态方法装饰器(staticmethod)、属性装饰器(property)等等。

为什么使用装饰器

人们可以使用其他辅助函数调用来实现装饰器的功能。但是装饰器为这样的任务提供了一种显式的语法,它使得意图明确,可以最小化扩展代码的冗余,并且有助于确保正确的使用:

1. 装饰器有一种非常明确的语法,这使得它们比那些可能任意地远离主体函数或类的辅助函数调用更容易为人们发现。

2. 当主体函数或类定义的时候,装饰器应用一次。在对类或函数的每次调用的时候,不必添加额外的代码。

函数装饰器

函数装饰器是一种关于函数的运行时声明,函数的定义需要遵守此声明。装饰器在紧挨着定义一个函数或方法的def语句之前的一行编写,并且它由@符号以及紧随其后装饰器名称组成。

在编码方面,函数装饰器自动将如下的语法:

映射为如下对等形式,其中decorator是一个可调用对象,它返回与 f 具有相同数目的参数的一个可调用对象。

装饰器自身是一个返回可调用对象的可调用对象。也就是说,它返回了一个对象,当随后装饰的函数被调用的时候,将会调用这个对象,这个对象可以是拦截了最初的函数之后调用的一个包装器对象,也可以是最初的函数以某种方式的扩展。

第一种方法是,用一种常用的编码模式:装饰器返回了一个包装器, 包装器把最初的函数保持到一个封闭的作用域中,在下面的例子中,当随后调用func函数的时候,其实是调用装饰器所返回的包装器函数;随后包装器函数可能会运行最初的func,因为它在一个封闭的作用域中仍然可以使用。当以这种方式编码的时候,每个装饰的函数都会产生一个新的作用域来保持状态。

还有第二种方法,为了对类做同样的事情,我们可以重载调用操作(__call__),并且能够使用实例属性而不是封闭的作用域。注意,这不是类装饰器!类装饰器是指用来装饰类的装饰器,而不是拿类来做装饰器。当按照这种方式编写代码的时候,每个装饰的函数都会产生一个新的实例来保持状态。

上面基于类的代码,尽管它对于拦截简单函数调用有效,但当它应用于类方法的时候,并不是很有效。因为当按照这种方式编码的时候,装饰的方法重绑定到装饰器类的一个实例,而不是一个简单的函数。这带来的问题是,当装饰器的__call__方法随后运行的时候,其中的self接收装饰器类实例,但是没有实例传递给*args。在这种情况下,使用第一种方法,可以正常工作,如果还需要类似上面的计数功能,可以在装饰器函数中使用 nonlocal 实现。

类装饰器

类装饰器与函数装饰器有相同的语法和非常相似的编码模式。但是,类装饰器不是包装单个函数,而是管理类的一种方式。

等同于下面的写法,类自动地传递给装饰器函数,并且装饰器的结果返回来分配给类名:

类装饰器的直接效果是,随后调用类名会创建一个实例,该实例会触发装饰器所返回的可调用对象,而不是调用最初的类自身。具体实现如下。

这样的一个类装饰器返回的可调用对象,通常创建并返回最初的类的一个新的实例,以某种方式来扩展对其接口的管理。例如,下面的例子插入一个对象来拦截一个类实例的未定义的属性:

在这个例子中,装饰器把类的名称重新绑定到另一个类(Wrapper),当调用它的时候,创建并嵌入了最初的类的一个实例。当随后从该实例获取一个属性的时候,包装器的__getattr__拦截了它,并且将其委托给最初的类的嵌入的实例。此外,每个被装饰的类都创建一个新的作用域,它记住了最初的类。

装饰器嵌套

有的时候,一个装饰器不够。为了支持多步骤的扩展,我们可以向一个函数添加多个装饰器,每个装饰器处理前一个的结果。当使用这一功能的时候,每个装饰器必须出现在自己的一行中,如下:

装饰器参数

函数装饰器和类装饰器似乎都能接受参数,尽管实际上这些参数传递给了真正返回装饰器的一个可调用对象,而装饰器反过来又返回一个可调用对象。例如,如下代码:

自动地映射到其对等的形式,其中装饰器是一个可调用对象,它返回实际的装饰器。返回的装饰器反过来返回可调用的对象,这个对象随后运行以调用最初的函数名。

可能是如下形式:

装饰器参数往往意味着可调用对象的3个层级:接受装饰器参数的一个可调用对象,它返回一个可调用对象以作为装饰器,该装饰器返回一个可调用对象来处理对最初的函数或类的调用。这3个层级的每一个都可能是一个函数或类,并且可能以作用域或类属性的形式保存了状态。

staticmethod装饰器

该装饰器返回函数的静态方法。静态方法为类所有,可以通过对象来使用,也可以通过类来使用。但一般建议通过类名来使用,因为静态方法只要定义了类,不必建立类的实例就可使用。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值