通俗讲解python 装饰器

作者:把苹果v咬哭 时间:2022-04-30 08:20:47 

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

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

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

一、什么是装饰器

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

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

放心,绝对不是"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]

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

来源:https://www.cnblogs.com/pingguo-softwaretesting/p/13522909.html

标签:python,装饰器
0
投稿

猜你喜欢

  • Python调用Jar包的两种方式小结

    2023-07-26 06:29:12
  • SQLServer日期函数总结案例详解

    2024-01-13 07:50:49
  • FP-growth算法发现频繁项集——构建FP树

    2022-04-26 16:22:52
  • pjblog3相关日志功能(支持生成静态模式)

    2008-11-20 13:41:00
  • javascript设计模式交流(一)Singleton Pattern

    2007-11-29 13:20:00
  • python聊天室(虽然很简洁,但是可以用)

    2021-05-21 01:10:46
  • python轻松办公将100个Excel中符合条件的数据汇总到1个Excel里

    2021-08-12 03:17:11
  • 探析Windows下将gvim8配置为Python IDE的方法

    2021-02-02 06:28:30
  • python的Crypto模块实现AES加密实例代码

    2022-10-02 17:43:10
  • Django models文件模型变更错误解决

    2021-05-14 11:02:10
  • Python实现批量word文档转pdf并统计其页码

    2023-08-19 13:29:40
  • 基于Golang实现内存数据库的示例详解

    2024-01-20 05:23:18
  • PHP:微信小程序 微信支付服务端集成实例详解及源码下载

    2023-11-14 13:37:55
  • mysql 5.7.19 winx64免安装版配置教程

    2024-01-14 00:27:36
  • 提升你设计水平的CSS3新技术[译]

    2009-08-02 20:51:00
  • c#实现数据库事务示例分享

    2024-01-28 05:03:02
  • Python分析彩票记录并预测中奖号码过程详解

    2023-07-20 04:49:18
  • 基于express中路由规则及获取请求参数的方法

    2024-05-11 10:17:53
  • 使用递归算法结合数据库解析成Java树形结构的代码解析

    2024-01-18 00:11:33
  • SQL Server 日期相关资料详细介绍

    2012-07-11 16:14:07
  • asp之家 网络编程 m.aspxhome.com