Python装饰器基础概念与用法详解


Posted in Python onDecember 22, 2018

本文实例讲述了Python装饰器基础概念与用法。分享给大家供大家参考,具体如下:

装饰器基础

前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自己编写新的装饰器的更多高级语法。

什么是装饰器

装饰是为函数和类指定管理代码的一种方式。Python装饰器以两种形式呈现:

【1】函数装饰器在函数定义的时候进行名称重绑定,提供一个逻辑层来管理函数和方法或随后对它们的调用。
【2】类装饰器在类定义的时候进行名称重绑定,提供一个逻辑层来管理类,或管理随后调用它们所创建的实例。

简而言之,装饰器提供了一种方法,在函数和类定义语句的末尾插入自动运行的代码——对于函数装饰器,在def的末尾;对于类装饰器,在class的末尾。这样的代码可以扮演不同的角色。

装饰器提供了一些和代码维护性和审美相关的有点。此外,作为结构化工具,装饰器自然地促进了代码封装,这减少了冗余性并使得未来变得更容易。

函数装饰器

通过在一个函数的def语句的末尾运行另一个函数,把最初的函数名重新绑定到结果。

用法

装饰器在紧挨着定义一个函数或方法的def语句之前的一行编写,并且它由@符号以及紧随其后的对于元函数的一个引用组成——这是管理另一个函数的一个函数(或其他可调用对象)。

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

@decorator
def F(arg):
  ...
F(99)

映射为这个对等形式:

def F(arg):
  ...
F = decorator(F)
F(99)

这里的装饰器是一个单参数的可调用对象,它返回与F具有相同数目的参数的一个可调用对象。

当随后调用F函数的时候,它自动调用装饰器所返回的对象。

换句话说,装饰实际把如下的第一行映射为第二行(尽管装饰器只在装饰的时候运行一次)

fun(6,7)
decorator(func)(6,7)

这一自动名称重绑定也解释了之前介绍的静态方法和property装饰器语法的原因:

class C:
  @staticmethod
  def meth(...):...
  @property
  def name(self):...

实现

装饰器自身是返回可调用对象的可调用对象。实际上,它可以是任意类型的可调用对象,并且返回任意类型的可调用对象:函数和类的任何组合都可以使用,尽管一些组合更适合于特定的背景。

有一种常用的编码模式——装饰器返回了一个包装器,包装器把最初的函数保持到一个封闭的作用域中:

def decorator(F):
  def wrapper(*args):
    # 使用 F 和 *args
    # 调用原来的F(*args)
  return wrapper
@decorator
def func(x,y):
  ...
func(6,7)

当随后调用名称func的时候,它确实调用装饰器所返回的包装器函数;随后包装器函数可能运行最初的func,因为它在一个封闭的作用域中仍然可以使用。

为了对类做同样的事情,我们可以重载调用操作:

class decorator:
  def __init__(self,func):
    self.func = func
  def __call__(self,*args):
    # 使用self.func和args
    # self.func(*args)调用最初的func
@decorator
def func(x,y):
  ...
func(6,7)

但是,要注意的是,基于类的代码中,它对于拦截简单函数有效,但当它应用于类方法函数时,并不很有效:

如下反例:

class decorator:
  def __init__(self,func):
    self.func = func
  def __call__(self,*args):
    # 调用self.func(*args)失败,因为C实例参数无法传递
class C:
  @decorator
  def method(self,x,y):
    ...

这时候装饰的方法重绑定到一个类的方法上,而不是一个简单的函数,这一点带来的问题是,当装饰器的方法__call__随后运行的时候,其中的self接受装饰器类实例,并且类C的实例不会包含到一个*args中。

这时候,嵌套函数的替代方法工作得更好:

def decorator:
  def warpper(*args):
    # ...
  return wrapper
@decorator
def func(x,y):
  ...
func(6,7)
class C:
  @decorator
  def method(self,x,y):
    ...
x = C()
x.method(6,7)

类装饰器

类装饰器与函数装饰器使用相同的语法和非常相似的编码方式。类装饰器是管理类的一种方式,或者用管理或扩展类所创建的实例的额外逻辑,来包装实例构建调用。

用法

假设类装饰器返回一个可调用对象的一个单参数的函数,类装饰器的语法为:

@decorator
class C:
  ...
x = C(99)

等同于下面的语法:

class C:
  ...
C = decorator(C)
x = C(99)

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

实现

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

def decorator(cls):
  class Wrapper:
    def __init__(self,*args):
      self.wrapped = cls(*args)
    def __getattr__(self,name):
      return getattr(self.wrapped,name)
  return Wrapper
@decorator
class C:        # C = decorator(C)
  def __init__(self,x,y):    # Run by Wrapper.__init__
    self.attr = 'spam'
x = C(6,7)        # 等价于Wrapper(6,7)
print(x.attr)

在这个例子中,装饰器把类的名称重新绑定到另一个类,这个类在一个封闭的作用域中保持了最初的类。

就像函数装饰器一样,类装饰器通常可以编写为一个创建并返回可调用对象的“工厂”函数。

装饰器嵌套

有时候,一个装饰器不够,装饰器语法允许我们向一个装饰器的函数或方法添加包装器逻辑的多个层。这种形式的装饰器的语法为:

@A
@B
@C
def f(...):
  ...

如下这样转换:

def f(...):
  ...
f = A(B(C(f)))

这里,最初的函数通过3个不同的装饰器传递,每个装饰器处理前一个结果。

装饰器参数

函数装饰器和类装饰器都能接受参数,如下:

@decorator(A,B)
def F(arg):
  ...
F(99)

自动映射到其对等形式:

def F(arg):
  ...
F = decorator(A,B)(F)
F(99)

装饰器参数在装饰之前就解析了,并且它们通常用来保持状态信息供随后的调用使用。例如,这个例子中的装饰器函数,可能采用如下形式:

def decorator(A,B):
  # 保存或使用A和B
  def actualDecorator(F):
    # 保存或使用函数 F
    # 返回一个可调用对象
    return callable
  return actualDecorator

以上,这是装饰器的基础知识,接下来将学习编写自己的装饰器

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python高并发异步服务器核心库forkcore使用方法
Nov 26 Python
使用Python进行新浪微博的mid和url互相转换实例(10进制和62进制互算)
Apr 25 Python
Python中序列的修改、散列与切片详解
Aug 27 Python
Python实现求两个csv文件交集的方法
Sep 06 Python
Diango + uwsgi + nginx项目部署的全过程(可外网访问)
Apr 22 Python
python 获取指定文件夹下所有文件名称并写入列表的实例
Apr 23 Python
Python中实例化class的执行顺序示例详解
Oct 14 Python
numpy 计算两个数组重复程度的方法
Nov 07 Python
python对csv文件追加写入列的方法
Aug 01 Python
Python enumerate函数遍历数据对象组合过程解析
Dec 11 Python
浅谈tensorflow之内存暴涨问题
Feb 05 Python
python实现学生信息管理系统(面向对象)
Jun 05 Python
python 文本单词提取和词频统计的实例
Dec 22 #Python
python 删除字符串中连续多个空格并保留一个的方法
Dec 22 #Python
Python函数装饰器实现方法详解
Dec 22 #Python
使用python对文件中的单词进行提取的方法示例
Dec 21 #Python
Python类装饰器实现方法详解
Dec 21 #Python
Python实现的字典排序操作示例【按键名key与键值value排序】
Dec 21 #Python
Python简单获取二维数组行列数的方法示例
Dec 21 #Python
You might like
探讨捕获php错误信息方法的详解
2013/06/09 PHP
主流PHP框架的优缺点对比分析
2014/12/25 PHP
Ajax提交表单时验证码自动验证 php后端验证码检测
2016/07/20 PHP
PHP中STDCLASS用法实例分析
2016/11/11 PHP
jquery $.ajax入门应用一
2008/11/19 Javascript
Wordpress ThickBox 添加“查看原图”效果代码
2010/12/11 Javascript
jquery easyui combobox模糊过滤(示例代码)
2013/11/30 Javascript
JS获取地址栏参数的几种方法小结
2014/02/28 Javascript
javascript实现Email邮件显示与删除功能
2015/11/21 Javascript
jQuery+css实现的tab切换标签(兼容各浏览器)
2016/01/28 Javascript
基于JQuery实现分隔条的功能
2016/06/17 Javascript
nodejs获取微信小程序带参数二维码实现代码
2017/04/12 NodeJs
nodejs批量下载图片的实现方法
2017/05/19 NodeJs
安装vue-cli报错 -4058 的解决方法
2017/10/19 Javascript
nodejs简单访问及操作mysql数据库的方法示例
2018/03/15 NodeJs
Python抓取框架Scrapy爬虫入门:页面提取
2017/12/01 Python
异步任务队列Celery在Django中的使用方法
2018/06/07 Python
Python 实现子类获取父类的类成员方法
2019/01/11 Python
python实现微信定时每天和女友发送消息
2019/04/29 Python
Pytorch 数据加载与数据预处理方式
2019/12/31 Python
Python3 selenium 实现QQ群接龙自动化功能
2020/04/17 Python
通过自学python能找到工作吗
2020/06/21 Python
Python3爬虫中关于中文分词的详解
2020/07/29 Python
Python使用cn2an实现中文数字与阿拉伯数字的相互转换
2021/03/02 Python
HTML5 Canvas玩转酷炫大波浪进度图效果实例(附demo)
2016/12/14 HTML / CSS
携程旅行网:中国领先的在线旅行服务公司
2017/02/17 全球购物
英国时尚优质的女装:Hope Fashion
2018/08/14 全球购物
英国最大的在线床超市:Bed Star
2019/01/24 全球购物
五十岁生日宴会答谢词
2014/01/15 职场文书
商铺租赁意向书
2014/04/01 职场文书
党员对照检查材料
2014/09/22 职场文书
java固定大小队列的几种实现方式详解
2021/07/15 Java/Android
Java反应式框架Reactor中的Mono和Flux
2021/07/25 Java/Android
Html5同时支持多端sdk的小技巧
2021/11/17 HTML / CSS
《废话连篇——致新手》——chinapizza
2022/04/05 无线电
Android实现图片九宫格
2022/06/28 Java/Android