第九篇、装饰器

一、阅读目录

首先:你是否还在为装饰器到底是什么苦苦发愁?是不是还在知乎,百度上面苦苦查询到底是什么,到底怎么用,苍天啊,那些长篇大论到底是什么啊,卧槽卧槽、语法糖? 没关系,来我来帮你缕缕什么是装饰器,额,或许你看到这篇标题,心中正嗤之以鼻,不就是个装饰器吗?老子。。。。可是你真的知道装饰器吗?不如往下看吧

 

复制代码
1、闭包
2、装饰器概念
3、不带参数的装饰器
4、原函数带参数的装饰器
5、带参数的装饰器
6、两个装饰器
7、装饰器的应用场景
View Code
复制代码

二、闭包

1、闭包:内部函数包含对外部作用域而非全局作用域的引用   

提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来喽,包起呦,包起来哇

 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr
 
        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素

2、闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

应用领域:延迟计算(原来我们是传参,现在我们是包起来)
from urllib.request import urlopen

def index(url):
def get():
return urlopen(url).read()
return get

baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))

 3、闭包函数的具体用噶

  a、是定义在函数内部的函数

  b、包含对外部作用域名字的引用,而不是对全局作用域名字的引用

如:

1
2
3
4
5
6
7
def a():
    x=111       
    def b():
        print(x)
    return b  #为了打破层级关系限制,可以在任意位置调用这个函数
c=a()         #为了打破层级关系限制,可以在任意位置调用这个函
c()

 上面就满足两个条件,一、在a中包含了b这个函数,二、引用了a中的局部变量

闭包小例子:

复制代码
def index(url):
    def get():
        return requests.get(url).text
    return get
aa=index()
aa("www.baidu.com")
View Code
复制代码

 

三、装饰器概念

 1、什么是装饰器?

装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

       强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
2、为何要用装饰器:
开放封闭原则:对修改封闭,对扩展开放

四、原函数不带参数的装饰器

先放代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
def timer(func):
    def wrapper():
        start=time.time()
        res=func()
        stop=time.time()
        print("run time is %s" %(stop-start))
        return res
    return wrapper
 
@timer
def index():
    time.sleep(3)
    print("welcome to index")
    return 123
 
index()

 上面执行顺序首先会从上倒下加载 timer函数,然后执行@timer这个装饰器,再把index这个函数名当成参数传递到timer函数中,然后执行wrapper函数,这里就是闭包

然后执行下面的index(),这里的index就是wapper,wapper加上括号就是执行wrapper函数的方法,然后执行start,执行到func()的时候,由于func就是index,所以指针

跳到装饰器执行下面原来index()中的方法,(注意index已经被改变了,现在没有index了),然后执行stop依次执行

五、原函数带参数的装饰器

在实际中,我们的装饰器可能应用到不同的函数中去,这些函数的参数都不一样,和函数传参是一样的,下面来看看如何将其应用到装饰器中去。

复制代码
import time
def timer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print("run time is %s" %(stop-start))
        return res
    return wrapper

@timer
def index(name):
    time.sleep(3)
    print("welcome to here %s" %name)
    return 123

res=index("pyrene")
print(res)
复制代码

 

 看着是不是和上面的类似,额、、、其实就是加上了参数,还不懂?好吧,下面解释

首先wrapper函数 中为什么要加参数?由于index已经被改变了,变成了wapper,所以你下面要执行,传入name,所以wapper中必须要有参数啦

func函数中为什么要有参数?那是因为func就是index,下面函数中有了参数,所以func里面也要有

六、使用两个装饰器

 当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键。这里还是用最简单的例子来进行说明。

 

复制代码
def outer2(func2):
    def inner2(*args,**kwargs):
        print('开始')
        r=func2(*args,**kwargs)
        print('结束')
        return r
    return inner2
 
def outer1(func1):
    def inner1(*args,**kwargs):
        print('start')
        r=func1(*args,**kwargs)
        print('end')
        return r
    return inner1
 
@outer2                                # 这里相当于执行了 f=outer1(f)  f=outer2(f),步骤如下
@outer1                                #1、f=outer1(f) f被重新赋值为outer1(1)的返回值inner1,
def f():                               #    此时func1为 f():print('f 函数')
    print('f 函数')                     #2、f=outer2(f) 类似f=outer2(inner1) f被重新赋值为outer2的返回值inner2
                                       #    此时func2 为inner1函数 inner1里面func1函数为原来的 f():print('f 函数')
                                                                          
f()                                    # 相当于执行 outer2(inner1)()

执行结果如下:
开始                                   # 在outer函数里面执行,首先打印 ‘开始 ’
start                                 # 执行func2 即执行inner1函数 打印 ‘start’
f 函数                                 # 在inner1函数里面执行 func1 即f()函数,打印 ‘f 函数’
end                                   # f函数执行完,接着执行inner1函数里面的 print('end')
结束                                   # 最后执行inner2函数里面的 print('结束')
复制代码

 

 

python执行代码的时候碰到两个装饰器解释过程:

将@outer1和f()先执行,函数outer1()将返回值inner1重新赋值给f,此时f指向的函数体是inner1的函数体,这里称f为新f

@outer2将新f当作参数传入inner2(),然后将返回值inner2重新赋值给f,此时f指向的函数体是inner2的函数体

 

执行过程和解释过程是相反的:从上到下的,先通过第一层@outer2 -----> @outer1 -------> f(),结合执行结果你就很明白了。

七、带参数的装饰器

啥?带参数的装饰器?可能你会有这样的疑问,没错就是有

复制代码
current_user={"user":None}
def auth(auth_type="file"):
    def deco(func):
        def wrapper(*args,**kwargs):
            if auth_type=="file":
                if current_user["user"]:
                    return func(*args,**kwargs)
                name=input("name").strip()
                passwd=input("password").strip()

                with open("db.txt",encoding="utf-8") as f:
                    user_idc=eval(f.read())
                if name in user_idc and passwd==user_idc[name]:
                    res=func(*args,**kwargs)
                    current_user["user"]=name
                    return res
                else:
                    print("user or passwd error")
            elif auth_type=="mysql":
                print("mysql")
            elif auth_type=="ldap":
                print("ldap")
            else:
                print("not valid auth_type")
        return wrapper
    return deco

@auth(auth_type="mysql")  #@deco  #index=deco(index)
def index():
    print("from index")


index()     #index=wrapper
复制代码

 

看过上面的代码是不是有点小蒙蔽?哈哈没关系,下面帮你分析

首先加载auth函数,然后执行装饰器,这里的@auth执行之后,你会发现其实就是执行@deco.执行@deco下面的index()其实就是wrapper(),然后后面的你就懂了吧,哈哈

至此关于装饰器神秘的面纱已经揭开了,是不是不再那么神秘了,可是装饰器会用了,你知道它到底什么用吗?

八、装饰器应用

 这里稍微提一点,具体的关注我看后面的文章

装饰器的强大是有目共睹的,提供了其他语言没有的便捷,其中装饰器可以用在面向对象修饰符、多线程、多进程、协程等很多方面~ 

 

 好啦,装饰器解释已经完毕,如果有不懂的欢迎下方留言,或者加我的qq3110436742探讨。如果有什么错误,来拍砖啊

 

posted @   pi-pi-miao-miao  阅读(306)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示