函数(三)闭包函数与装饰器

摘要:
1.闭包函数什么是闭包函数定义在函数内部的函数,并且内部函数会引用外部函数的变量并且外部函数会返回内部函数的函数名(内函数的地址)闭包函数的意义要想使用内部函数,必须通过外部函数,相当于把内函数包住了直接传参defindex():x=1print(x)index()简单的一个闭包函数defouter():x=1#外部函数的变量definner():print(x)#内部函数引用了外部函数的变量re
1.闭包函数

什么是闭包函数

定义在函数内部的函数,并且内部函数会引用外部函数的变量并且外部函数会返回内部函数的函数名(内函数的地址)

闭包函数的意义

要想使用内部函数,必须通过外部函数,相当于把内函数包住了

直接传参

defindex():
    x = 1
    print(x)
index()

函数(三)闭包函数与装饰器第1张

简单的一个闭包函数

defouter():
    x = 1  #外部函数的变量
    definner():
        print(x)  #内部函数引用了外部函数的变量
    return inner  #外部函数返回了内部函数的函数名
res =outer()
res()

函数(三)闭包函数与装饰器第2张

requests模块

可以做一个简单的小爬虫模块

importrequests
defindex(url):
    response =requests.get(url)
    if response.status_code == 200:
        print(len(response.text))
index('https://www.baidu.com')

函数(三)闭包函数与装饰器第3张

2.装饰器简介

可调用的:callable

简单的来说就是可以加括号()的,不仅函数是可调用的,装饰器也是可调用的

什么是装饰器

给被装饰对象添加新功能的工具

为什么要用装饰器

开发封闭原则:对扩展开放,对修改封闭

具体的来说就是:不改变源代码的情况下增加新的功能

装饰器必须遵循的两个规则

1.不改变源代码

2.不改变源代码的调用方式

3.一个简单的装饰器

下面来输出一个简单的装饰器

defindex(x):
    print(x)
    return 'index'index(1)  #这是原来函数输出的

def outer(func):  #这里的func在下面被index替代
    print(111)
    def inner(*args,**kwargs):
        print(222)
        res = func(*args,**kwargs)  #这里相当于index()
        returnres
    returninner

index = outer(index)  #括号里的index是上面函数名index,前面的index是重新定义的一个变量名,跟之前的没有任何关系,index相当于inner
index(2)  #这是新函数输出的,这相当于inner(2)

函数(三)闭包函数与装饰器第4张

"1"是第一个index(1)输出的值,后面的是index(2)输出的值,这即表现出了装饰器不改变代码和调用方式的情况下可以增加功能

函数(三)闭包函数与装饰器第5张

这是运行的顺序图,函数只要遇到括号()就会执行函数体代码,所以这里的顺序是这样的

统计函数运行时间,time模块

defindex():
    print('这是index')

importtime
defouter(func):
    print('这是outer')
    definner():
        time1 = time.time()  #获取时间戳,离1970-01-01,00:00:00有多少秒
func()
        time.sleep(3)  #让cpu暂停三秒
        print('这是inner')
        time2 =time.time()
        print(time2 - time1)  #两个时间戳相减等于使用多少时间
    returninner

index =outer(index)
index()

函数(三)闭包函数与装饰器第6张

4.装饰器升级版

在函数中加入不定长参数*args和**kwargs可以使函数传入任意数量的参数

def index(name,age = 18):
    print(name,age)
    return 'index函数的返回值'

defouter(func):
    print('这是outer')
    def inner(*args,**kwargs):  #使用不定长参数可以使装饰器传任意的值
        print('这是inner')
        res = func(*args,**kwargs)
        print(res)  #使用res可以返回原函数的返回值
        returnres
    returninner

index =outer(index)
index('sxc',age = 19)

函数(三)闭包函数与装饰器第7张

5.装饰器语法糖

语法糖的用法:把装饰器提到前面,然后在被装饰的原函数前加@+装饰器名

还是使用上面的函数

defouter(func):
    print('这是outer')
    def inner(*args,**kwargs):  #使用不定长参数可以使装饰器传任意的值
        print('这是inner')
        res = func(*args,**kwargs)
        print(res)  #使用res可以返回原函数的返回值
        returnres
    returninner

@outer
def index(name,age = 18):
    print(name,age)
    return 'index函数的返回值'index('sxc',age = 19)

这样我们可以用一行代码@outer返回原来的index = outer(index),变量重新赋值的问题

函数(三)闭包函数与装饰器第8张

注意:语法糖和被装饰对象在书写的时候在紧紧的挨在一起,不能有空行

6.装饰器模板和认证装饰器

装饰器模版

defouter(func):
    def inner(*args,**kwargs):  #可以传任何参数
        #执行原函数之前的代码
        res = func(*args,**kwargs)  #执行原函数
        #执行原函数之后的代码
        return res  #返回原函数的值
    return inner

认证装饰器

importtime
user_login ={'islogin':False}
defouter(func):
    def inner(*args,**kwargs):
        if not user_login['islogin']:  #判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
            username = input('请输入您的用户名>>>:')
            pwd = input('请输入您的密码>>>:')
            if username == 'sxc' and pwd =='123':
                print('登录成功!')
                user_login['islogin'] =True
                res = func(*args,**kwargs)
                returnres
            else:
                print('用户名或密码错误')
        else:
            user_login['islogin'] =True
            res = func(*args,**kwargs)
            returnres
    returninner

@outer
def index():  #执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@outer
def index2():  #执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()

函数(三)闭包函数与装饰器第9张

这个认证装饰器可以判断用户是否已经登录,并且登录操作只需进行一次,下面的函数块都不会进行登录操作

7.多层装饰器

一般见到的多层装饰器都是在装饰器后再套一层装饰器,最外层函数也能传参,这样可以执行更多的功能

importtime
user_login ={'islogin':False}
defout_outer(today):
    #再套一层装饰器的原因是可以传入更多的参数,从而执行更多的功能
    defouter(func):
        def inner(*args,**kwargs):
            if today == 'monday':
                if not user_login['islogin']:  #判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
                    username = input('请输入您的用户名>>>:')
                    pwd = input('请输入您的密码>>>:')
                    if username == 'sxc' and pwd =='123':
                        print('登录成功!')
                        user_login['islogin'] =True
                        res = func(*args,**kwargs)
                        returnres
                    else:
                        print('用户名或密码错误')
                else:
                    user_login['islogin'] =True
                    res = func(*args,**kwargs)
                    returnres
            else:
                print('今天你不能登录')
        returninner
    returnouter

@out_outer('sunday')  #可以传更多的参数,可以进行更多的判断,执行更多的功能
def index():  #执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@out_outer('monday')
def index2():  #执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()importtime
user_login ={'islogin':False}
defout_outer(today):
    #再套一层装饰器的原因是可以传入更多的参数,从而执行更多的功能
    defouter(func):
        def inner(*args,**kwargs):
            if today == 'monday':
                if not user_login['islogin']:  #判断用户是否已经登录,如登录直接进入,未登录则进行登录操作
                    username = input('请输入您的用户名>>>:')
                    pwd = input('请输入您的密码>>>:')
                    if username == 'sxc' and pwd =='123':
                        print('登录成功!')
                        user_login['islogin'] =True
                        res = func(*args,**kwargs)
                        returnres
                    else:
                        print('用户名或密码错误')
                else:
                    user_login['islogin'] =True
                    res = func(*args,**kwargs)
                    returnres
            else:
                print('今天你不能登录')
        returninner
    returnouter

@out_outer('sunday')  #可以传更多的参数,可以进行更多的判断,执行更多的功能
def index():  #执行本函数时未登录
    time.sleep(1)
    print('未登录,登录完成后进入本页面')
index()

@out_outer('monday')
def index2():  #执行本函数时已登录
    time.sleep(3)
    print('已登录,直接进入本页面')
index2()

函数(三)闭包函数与装饰器第10张

不是只能传一个参数,还能传更多的参数

装饰器执行的顺序:装饰时从下往上,执行时从上往下

defoutter1(func1):
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        returnres1
    returnwrapper1
defoutter2(func2):
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        returnres2
    returnwrapper2
defoutter3(func3):
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        returnres3
    returnwrapper3
@outter1  #index = outter1(wapper2)
@outter2  #wrapper2 = outter2(wrapper3)
@outter3  #wrapper3 = outter3(最原始的index函数内存地址)
defindex():
    print('from index')
index()

函数(三)闭包函数与装饰器第11张

函数(三)闭包函数与装饰器第12张

注意:

装饰器在装饰的时候 顺序从下往上
装饰器在执行的时候 顺序从上往下

8.装饰器的补充(完善装饰器)
defouter(func):
    '''这是outer的注释
    :param func:
    :return:
    '''
    def inner(*args,**kwargs):  #可以传任何参数
        '''这是inner的注释
        :param args:
        :param kwargs:
        :return:
        '''
        #执行原函数之前的代码
        res = func(*args,**kwargs)  #执行原函数
        #执行原函数之后的代码
        return res  #返回原函数的值
    returninner

@outer
def index():  #原函数
    '''这是index的注释
    :return:
    '''
    return '原函数的返回值'

print(help(index))  #当我们在打印index的注释的时候会打印装饰器内index指代的inner的注释
print(index.__name__)  #查看函数名字符串形式,这也会打印inner

函数(三)闭包函数与装饰器第13张

打印注释和函数名字符串形式的时候都会打印原字符串,虽然功能没破坏,但是违背了我们不想让用户察觉的原则

这时候我们就可以使用(from functools import wraps)内置方法改善装饰器

函数(三)闭包函数与装饰器第14张

在装饰器外部声明这个方法,再在装饰器外层调用这个方法

输出结果

函数(三)闭包函数与装饰器第15张

这样就可以实现:

用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释

11

免责声明:文章转载自《函数(三)闭包函数与装饰器》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇unity resources文件夹作用XNA框架基础——XNA介绍下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

sklearn中的数据集的划分

sklearn数据集划分方法有如下方法: KFold,GroupKFold,StratifiedKFold,LeaveOneGroupOut,LeavePGroupsOut,LeaveOneOut,LeavePOut,ShuffleSplit,GroupShuffleSplit,StratifiedShuffleSplit,PredefinedSplit,...

vue项目----购物车商品的删除

1.给删除链接,绑定删除的函数,因为是a链接,要阻止默认行为。  2.再添加removegoods(item.id,index)函数 分析:我们通过item.id删除把商品从store中删除            通过index删除把商品从当前的组件中的shopcargoodslist中删除。 3.要想删除state里面的数据,不能直接操作,要在muta...

001.mysql-炸裂函数的实现

SELECTsubstring_index(substring_index(a.chain,'_',b.help_topic_id + 1 ), '_' ,- 1 ) ASID FROM(select '1_11_1223_1242' aschain) a JOIN mysql.help_topic b ON b.help_topic...

js数组遍历方法总结

js数组遍历方法总结  数组遍历方法 1.for循环 使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。 1 2 3 for(j = 0,len=arr.length; j < len; j++) {      } 2.foreach循环 遍历数组中的每一项,没有返回值,对原数组没有影响,不支持...

GCC 中的编译器堆栈保护技术(转)

转自:https://www.cnblogs.com/gt-xy/p/7749725.html 前几天看到的觉得不错得博客于是转发了,但这里我补充一下一些点。 GCC通过栈保护选项-fstack-protector-all编译时额外添加两个符号,__stack_chk_guard和__stack_chk_fail分别是存储canary word值的地址...

Gradle入门到实战(一) — 全面了解Gradle

声明:本文来自汪磊的博客,转载请注明出处 可关注个人公众号,那里更新更及时,阅读体验更好:  友情提示由于文章是从个人公众号拷贝过来整理的,发现图片没有正常显示,没关注公众号的同学可通过如下链接查看:https://mp.weixin.qq.com/s?__biz=Mzg2NzAwMjY4MQ==&mid=2247483789&idx=1...