Python 装饰器

装饰器

什么是装饰器

装饰器顾名思义就是一个有装饰功能的工具,那么装饰器又是用来装饰什么的?为什么要装饰这个东西?装饰的目的是什么呢?本文会一一作答让小弟一个一个说

开放封闭原则

谈及装饰器就要引申一个概念,那就是开放封闭原则,那么问题又来了 什么是开放封闭……好好好,直接说这个,开放封闭本来是两个对立的概念,也就是说是一对反义词,那么为什么要提出开放封闭原则呢?原因是在日常的开发工作中,一般最初上线的产品的功能是不尽完善的,就是说不够完美但已经能够支撑日常使用,其余的功能可以日后扩展,举例:N年前的QQ和现在的QQ(PS:虽然现在本人不怎么用了)。在后期扩展功能时,因为函数已经是写好的,而且存在大量调用,所以要直接去给函数增添新的功能显然不现实,所以我们只能新建函数去给原来的函数扩展功能(其实这就是一个装饰器啦)

那么开放封闭原则到底是什么? 答案是对源码封闭,对新功能开放

  • 封闭原则:不要改变源代码

  • 开放原则:能增加一些额外的功能

    如果还有客观对开放封闭原则似知似解的话,没关系接着往下看,不影响您食用本文,因为Python装饰器本身就是对开放封闭原则完美的诠释

装饰器初识

不低调的说,装饰器就是一个函数,名字本来很高大上,但本质就是一个函数,装饰器函数的功能就是要装饰一个函数,在不改变被装饰函数的源代码及调用方式的前提下,为其增加额外的功能。是不是有点门道了,是不是觉得这玩意也没啥啦,真是优秀的同学。

代码show(技术博客不写代码干白话说出去丢人)

def warpper(f):     #定义一个函数(装饰器),传入的参数是被修饰的函数的函数名

def inner(*args,**kwargs): #嵌套一个内存函数,这个函数主题才是执行被装饰函数源码的关键

    '''这块可以加要在被装饰函数执行之前的操作哈'''

    ret = f(*args,**kwargs) #这里的形参我会在下面说明

    '''这里可以写被装饰函数之后的,兄嘚,别客气,想加啥方法加什么'''

    return ret #这里的返回值如果我一会儿不忘的话也会在下面说明

return inner

@warpper #这个叫语法糖,嗯……可以吃(可能老外命名的时候就是这么想的),结构是@加函数名,作用下面会说明
def func():
  print('我就是那个被装饰的函数')
简单说明

因为本人比较懒,所以原谅我直接把代码甩上去了,后面有注释,看懂了的大佬可以say goodbye啦,想打我的接着听我白话,那我就把备注再重复一遍,哈哈,你也看到了,装饰器用到了函数的嵌套(再具体点就是闭包,要问什么是闭包,百度吧,哈哈),首先外层函数接收到一个函数名,然后返回值是内层函数的函数名;再来内层函数可以接收参数,其中ret = f(*args,**kwargs)有两个作用,一是执行了传入的函数,也就是执行了被修饰的函数,二是将返回值赋给了一个变量,此处要说明一下,对被修饰的函数功能的扩展要写在这里哦,最后 ret作为内层函数的返回值返回给函数执行者。

语法糖

@warpper这东西和 func = wrapper(func)是一样的,也就是说最后三行代码可以这样写

#@warpper   
def func():
  print('我就是那个被装饰的函数')
func = wrapper(func)   #注意奥,这玩意要写在被装饰函数的下边,语法糖才写上边

至于为什么要写着东西,或者为什么要用语法糖?请听下回分解~~~,收起你滴拳头,是这样,我刚开始的时候谈到了,装饰器是要在不改变源码和其调用方式的前提下给其增加新的功能,注意到了么 调用方式 嗯……没错就是调用方式,如果我不这样写那我是不是要wrapper(func)()这样去调用啊,是不是有点绕了,但是我把wrapper(func)赋值给了一个和被装饰函数同名的变量,那我此时要怎么调用,是不是就是func(),这样就满足了开放封闭原则,完美!其实本质就是要把装饰器“伪装”成原函数,包括调用方式、参数、返回值,装就要装的像一点,对吧。

装饰带参数的函数
def wrapper(f):
def inner(*args,**kwargs):
f(*args,**kwargs)
return inner
@wrapper
def func(a,b):
print(a,b)

函数func中有两个形参a和b,说一下这两个参数在装饰器中的旅程,函数inner的万能参数接收到a和b打包,然后inner函数充当中间商将打包后的元组给了f,在f中打散又成了变量a和b,在装饰器时就不会影响传参了,这就是装饰带参数的函数

装饰有返回值的函数

def wrapper(f):
def inner():
ret = f()
return ret
return inner
@wrapper
def func():
return('我不管,我最帅')

哈哈,这段代码中有我的心声,你们都懂的,很简单,装饰有返回值的参数,在inner函数中将f()赋值给ret,这样ret就接收到了返回值,再将ret返回给inner(),以此来达到“模拟”被装饰函数的返回值,也可以说是通过这种方法来拿到被装饰函数的返回值。

装饰器带参数

先举个例子,当我们需要写一个简单的登陆认证功能的时候,我们的目的是要用装饰器给调用的函数增加一个认证是否登陆过此网站的功能,也就是要验证用户名和密码,但比如不同公司的网站数据库肯定并非同一个,这个时候就需要带参数的装饰器啦,它可以实现我们要用一个装饰器装饰多个类似函数的目的

def wrapper_out(n):
   def wrapper(f):
       def inner(*args,**kwargs):
           with open(n,encoding='utf-8') as f1:
           '''此部分省略认证的详细功能'''
           f(*args,**kwargs)
       return inner
   return wrapper

@wrapper_out('webpage1')
def wangzhi1():
   print('欢迎访问网址1')
   
@wrapper_out('webpage2')
def wangzhi1():
   print('欢迎访问网址2')

如果使用标准的装饰器函数的话只能装饰其中的一个函数,当装饰另一个函数时会因为访问不到正确的数据库而报错。

@wrapper_out('webpage1')这段代码先执行wrapper_out('webpage1')这个函数先把参数webpage传给n,并且返回一个wrapper,此时@和wrapper结合在一块有没有很眼熟,没错,这就是我们所熟悉的标准的装饰器了,余下的流程就和标准的装饰器完全相同了。

多个装饰器装饰一个函数

这是一种特殊的情况,下面重点分析这种情况的结果是如何产生的,会有点绕。

def wrapper1(func1):
    def inner1():
        print('wrapper1 ,before func')
        func1()
        print('wrapper1 ,after func')
    return inner1

def wrapper2(func2): # func2 == inner1
    def inner2():
        print('wrapper2 ,before func')
        func2() # inner1
        print('wrapper2 ,after func')
    return inner2

@wrapper2  
@wrapper1  
def f():
print('in f') # 3

f()  

结果:

wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func

怎么说?是不是和你预期的结果有所不同,下面来按步骤说一下为什么会产生这样的结果(我尽量表达清楚哈)

1. 函数定义不调用不执行直接pass
2.   @wrapper2
@wrapper1  
def f():
单独看下面这两行,标准装饰器哈 @wrapper1 等价于 f = wrapper1(f) = inner1(返回值哈)
3. @wrapper2
  @wrapper1   #注意哈 看步骤2 这里现在是inner1咯、
  @wrapper2 等价于 inner1 = wrapper2(inner1) = inner2(返回值)
4. 此时的 f = inner2 执行f() 就从inner2()开始执行
5. 执行inner2 首先打印'wrapper2 ,before func'
6. 然后执行func2,由步骤3可知当前wrapper2中的参数是inner1 也就是执行inner1 所以打印wrapper1 ,before func
7. 然后执行func1 参考步骤2 可知这里的func1()执行的是f()也就是真正的原函数,打印in f
8. 顺序执行,打印wrapper1 ,after func
9. inner1执行完(其实就是func2执行完)还是顺序执行 打印wrapper2 ,after func
10. 结果出来了、哈哈

这部分本人能力也只能写成这样了,各位看官看不懂那一定是小弟没表述清楚,不过没关系,在下再支一招,看图

结合结果分析,相信各位老板也能推导出正确的结果了,嗯、真帅!

好,到此对Python装饰器应该有那么一丢丢的认识了哈,我不管,就得有认识。下面的内容和文章关系不大哈

第一篇正经写的文章,可能文章内容表达不尽如人意的正经哈,但初心是好的,就是分享知识,分享心得嘛,嗯,不管怎么说,我还是很欣慰的对自己,哈哈,能有人从中有收获就更perfect喽

回到顶部

原文地址:https://www.cnblogs.com/muchengQ/p/11078760.html