通俗讲解python 装饰器


Posted in Python onSeptember 07, 2020

装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。

其实在平常写写脚本的过程中,这个知识点你可能用到不多

但在面试的时候,这可是一个高频问题。

一、什么是装饰器

所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。

放心,绝对不是"Hello World"!

def hello():
  print("你好,装饰器")

肿么样,木骗你吧? 哈哈,这个函数不用运行相信大家都知道输出结果:"你好,装饰器"。

那如果我想让hello()函数再实现个其他功能,比如多打印一句话。

那么,可以这样"增强"一下:

def my_decorator(func):
  def wrapper():
    print("这是装饰后具有的新输出")
    func()
  return wrapper

def hello():
  print("你好,装饰器")

hello = my_decorator(hello)

hello()

运行结果:

这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]

很显然,这个"增强"没啥作用,但是可以帮助理解装饰器。

当运行最后的hello()函数时,调用过程是这样的:

  1. hello = my_decorator(hello)中,变量hello指向的是my_decorator()
  2. my_decorator(func)中传参是hello,返回的wrapper,因此又会调用到原函数hello()
  3. 于是乎,先打印出了wrapper()函数里的,然后才打印出hello()函数里的

那上述代码里的my_decorator()就是一个装饰器。
它改变了hello()的行为,但是并没有去真正的改变hello()函数的内部实现。

但是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。

二、优雅的装饰器

所以,想让上述装饰器变得优雅,可以这样写:

def my_decorator(func):
  def wrapper():
    print("这是装饰后具有的新输出")
    func()
  return wrapper

@my_decorator
def hello():
  print("你好,装饰器")

hello()

这里的@my_decorator就相当于旧代码的hello = my_decorator(hello),@符号称为语法糖。

那如果还有其他函数也需要加上类似的装饰,直接在函数的上方加上@my_decorator就可以,大大提高函数
的重复利用与可读性。

def my_decorator(func):
  def wrapper():
    print("这是装饰后具有的新输出")
    func()
  return wrapper

@my_decorator
def hello():
  print("你好,装饰器")

@my_decorator
def hello2():
  print("你好,装饰器2")

hello2()

输出:

这是装饰后具有的新输出
你好,装饰器2
[Finished in 0.1s]

三、带参数的装饰器

1. 单个参数

上面的只是一个非常简单的装饰器,但是实际场景中,很多函数都是要带有参数的,比如hello(people_name)。

其实也很简单,要什么我们就给什么呗,直接在对应装饰器的wrapper()上,加上对应的参数:

def my_decorator(func):
  def wrapper(people_name):
    print("这是装饰后具有的新输出")
    func(people_name)
  return wrapper

@my_decorator
def hello(people_name):
  print("你好,{}".format(people_name))

hello("张三")

输出:

这是装饰后具有的新输出
你好,张三
[Finished in 0.1s]

2. 多个参数

但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的,
当其他要使用装饰器的函数参数不止这个一个肿么办?比如:

@my_decorator
def hello3(speaker, listener):
  print("{}对{}说你好!".format(speaker, listener))

没关系,在python里,*args**kwargs表示接受任意数量和类型的参数,所以我们可以这样
写装饰器里的wrapper()函数:

def my_decorator(func):
  def wrapper(*args, **kwargs):
    print("这是装饰后具有的新输出")
    func(*args, **kwargs)
  return wrapper

@my_decorator
def hello(people_name):
  print("你好,{}".format(people_name))

@my_decorator
def hello3(speaker, listener):
  print("{}对{}说你好!".format(speaker, listener))

hello("老王")
print("------------------------")
hello3("张三", "李四")

同时运行下hello("老王"),和hello3("张三", "李四"),看结果:

这是装饰后具有的新输出
你好,老王
------------------------
这是装饰后具有的新输出
张三对李四说你好!
[Finished in 0.1s]

3. 自定义参数

上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。
比如,我加个参数来控制下装饰器中打印信息的次数:

def count(num):
  def my_decorator(func):
    def wrapper(*args, **kwargs):
      for i in range(num):
        print("这是装饰后具有的新输出")
        func(*args, **kwargs)
    return wrapper
  return my_decorator

@count(3)
def hello(people_name):
  print("你好,{}".format(people_name))

hello("老王")

注意,这里count装饰函数中的2个return.
运行下,应该会出现3次:

这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
[Finished in 0.1s]

4. 内置装饰器@functools.wrap

现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:

def my_decorator(func):
  def wrapper(*args, **kwargs):
    print("这是装饰后具有的新输出")
    func(*args, **kwargs)
  return wrapper

@my_decorator
def hello(people_name):
  print("你好,{}".format(people_name))

print(hello.__name__) #看下hello函数的元信息

输出:

wrapper

这说明了,它不再是以前的那个 hello() 函数,而是被 wrapper() 函数取代了。

如果我们需要用到元函数信息,那怎么保留它呢?这时候可以用内置装饰器@functools.wrap

import functools

def my_decorator(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
    print("这是装饰后具有的新输出")
    func(*args, **kwargs)
  return wrapper

@my_decorator
def hello(people_name):
  print("你好,{}".format(people_name))

print(hello.__name__)

 运行下:

hello
[Finished in 0.1s]

四、类装饰器

装饰器除了是函数之外,也可以是类。

但是类作为装饰器的话,需要依赖一个函数__call__(),当调用这个类的实例时,函数__call__()就
会被执行。

来改造下之前的例子,把函数装饰器改成类装饰器:

class MyDecorator():
  def __init__(self, func):
    self.func = func

  def __call__(self, *args, **kwargs):
    print("这是装饰后具有的新输出")
    return self.func(*args, **kwargs)

# def my_decorator(func):
#   def wrapper():
#     print("这是装饰后具有的新输出")
#     func()
#   return wrapper

@MyDecorator
def hello():
  print("你好,装饰器")

hello()

运行:

这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]

跟函数装饰器一样,实现一样的功能。

五、装饰器的嵌套

既然装饰器可以增强函数的功能,那如果有多个装饰器,我都想要怎么办?
其实,只要把需要用的装饰器都加上去就好了:

@decorator1
@decorator2
@decorator3
def hello():
  ...

但是要注意这里的执行顺序,会从上到下去执行,可以来看下:

def my_decorator(func):
  def wrapper():
    print("这是装饰后具有的新输出")
    func()
  return wrapper

def my_decorator2(func):
  def wrapper():
    print("这是装饰后具有的新输出2")
    func()
  return wrapper

def my_decorator3(func):
  def wrapper():
    print("这是装饰后具有的新输出3")
    func()
  return wrapper

@my_decorator
@my_decorator2
@my_decorator3
def hello():
  print("你好,装饰器")

hello()

运行

这是装饰后具有的新输出
这是装饰后具有的新输出2
这是装饰后具有的新输出3
你好,装饰器
[Finished in 0.1s]

好记性不如烂笔头,写一下理解一下会好很多。

以上就是通俗讲解python 装饰器的详细内容,更多关于python 装饰器的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python调用windows api锁定计算机示例
Apr 17 Python
python编写Logistic逻辑回归
Dec 30 Python
Python 创建空的list,以及append用法讲解
May 04 Python
Python3.6基于正则实现的计算器示例【无优化简单注释版】
Jun 14 Python
Django框架实现分页显示内容的方法详解
May 10 Python
python实现批量修改服务器密码的方法
Aug 13 Python
djano一对一、多对多、分页实例代码
Aug 16 Python
python实现的批量分析xml标签中各个类别个数功能示例
Dec 30 Python
jupyter notebook tensorflow打印device信息实例
Apr 20 Python
python 一维二维插值实例
Apr 22 Python
使用Python FastAPI构建Web服务的实现
Jun 08 Python
python实现三壶谜题的示例详解
Nov 02 Python
彻底搞懂python 迭代器和生成器
Sep 07 #Python
python如何设置静态变量
Sep 07 #Python
获取CSDN文章内容并转换为markdown文本的python
Sep 06 #Python
浅谈Python描述数据结构之KMP篇
Sep 06 #Python
详解Python3 定义一个跨越多行的字符串的多种方法
Sep 06 #Python
Python中实现一行拆多行和多行并一行的示例代码
Sep 06 #Python
Pytest单元测试框架如何实现参数化
Sep 05 #Python
You might like
phpize的深入理解
2013/06/03 PHP
php隐藏IP地址后两位显示为星号的方法
2014/11/21 PHP
PHP中mysql_field_type()函数用法
2014/11/24 PHP
php 微信公众平台开发模式实现多客服的实例代码
2016/11/07 PHP
jquery控制listbox中项的移动并排序
2009/11/12 Javascript
javascript使用百度地图api和html5特性获取浏览器位置
2014/01/10 Javascript
js文件包含的几种方式介绍
2014/09/28 Javascript
jQuery中mouseover事件用法实例
2014/12/26 Javascript
js的for in循环和java里foreach循环的区别分析
2015/01/28 Javascript
javaScript实现滚动新闻的方法
2015/07/30 Javascript
基于jQuery实现收缩展开功能
2016/03/18 Javascript
jQuery文字提示与图片提示效果实现方法
2016/07/04 Javascript
AngularJS中$http服务常用的应用及参数
2016/08/22 Javascript
Vue.js路由组件vue-router使用方法详解
2016/12/02 Javascript
微信小程序 利用css实现遮罩效果实例详解
2017/01/21 Javascript
JQuery用$.ajax或$.getJSON跨域获取JSON数据的实现代码
2017/09/23 jQuery
React操作真实DOM实现动态吸底部的示例
2017/10/23 Javascript
vue-cli中vue本地实现跨域调试接口
2019/01/16 Javascript
Openlayers显示瓦片网格信息的方法
2020/09/28 Javascript
用Python进行一些简单的自然语言处理的教程
2015/03/31 Python
Python中的ctime()方法使用教程
2015/05/22 Python
python实现网页自动签到功能
2019/01/21 Python
Django 响应数据response的返回源码详解
2019/08/06 Python
在Pytorch中计算卷积方法的区别详解(conv2d的区别)
2020/01/03 Python
用Python做一个久坐提醒小助手的示例代码
2020/02/10 Python
python Gabor滤波器讲解
2020/10/26 Python
汇集了世界上最好的天然和有机美容产品:LoveLula
2018/02/05 全球购物
Wojas罗马尼亚网站:波兰皮鞋品牌
2018/11/01 全球购物
动物学专业毕业生求职信
2013/10/11 职场文书
电子商务专业求职信
2014/03/08 职场文书
合同协议书格式
2014/04/18 职场文书
网络营销计划
2015/01/17 职场文书
车间主任岗位职责范本
2015/04/08 职场文书
村党总支部公开承诺书2016
2016/03/25 职场文书
Python编程中Python与GIL互斥锁关系作用分析
2021/09/15 Python
Ajax异步刷新功能及简单案例
2021/11/20 Javascript