• 函数值之装饰器


    7.1 装饰器

      本质就是一个python函数,它可以在不改变函数任何代码的前提下,增加函数额外的功能

      例如:记录日志,性能测试,用户登陆

      装饰器的返回值也是一个函数对象。

    7.2 装饰器形成

    测试该函数执行时间:

    import time
    
    
    def func1():
        print('in func1')
    
    
    def timer(func):
        def inner():
            start_time = time.time()
            time.sleep(0.1)
            func()
            end_time = time.time()
            print('效率: %s' % (end_time - start_time))
        return inner
    
    
    func1 = timer(func1)  
    func1()         

     7.2.1 装饰器的语法糖 @(基本语法)

    import time
    def timer(func):              #1 timer(func1),调用该函数
        def inner():              #3
            start = time.time()   #6
            func()                #7
            print(time.time() - start)  #9
        return inner              #4
    
    @timer                        #2  func1 = timer(func1)
    def func1():
        print('in func1')         #8
    func1()                       #5
    
    # 这一个函数自上而下,先找哪里调用了它,先来到@timer,执行timer(func1),来到inner函数这里,
    # 但没有调用它的方法(inner()),所以跳过这个函数直接到return inner(注意这里return的是inner,
    # 这个很关键,父级返回子级,但是没有调用),接下来再从@timer以下去找哪里还有调用函数,找到func1(),
    # 现在func1()其实是timer(func1)(),(不懂看下面这个小的例子,自己试试),外层函数执行完返回inner,
    # 现在就是调用inner函数了,即timer(func1)() = inner().inner函数体内又包含一个函数的调用(func()),
    # 此时的func()由于timer(func1)这里形参赋值变成了func1(),在#7 这里对函数进行了调用,执行函数func1,
    # 打印#8,最后打印#9。 
    """
    def fun():
    	a = 41
    	def fun1():
    		print(a, '啦啦啦')
    	return fun1
    
    fun()()
    """  

    7.2.2 被装饰函数带有参数的装饰器

    import time
    def timer(func):
        def inner(*args, **kwargs):
            start = time.time()
            re = func(*args, **kwargs)
            end = time.time()
            print(end - start)
            return re    # 我擦,又返回了这个func(*args, **kawrgs)
        return inner
    
    @timer   # ==> jjj = timer(jjj)
    def jjj(a):
        print('in jjj and get a:%s' % (a))
        return 'fun2 over'
    
    
    jjj('aaaaaa')
    print(jjj('aaaaaa'))    # 运行这步fun2 over 才打印了

    ---in jjj and get a:aaaaaa
    ---0.0
    ---in jjj and get a:aaaaaa
    ---0.0
    ---fun2 over

     7.2.3带有多个参数的装饰器

    def wrapper1(func):  
        def inner1():
            print('wrapper1 ,before func')  # 2
            func()        # 现在这里变成了f()
            print('wrapper1 ,after func')  # 4
        return inner1
    
    def wrapper2(func):  
        def inner2():
            print('wrapper2 ,before func')  # 1
            func()       # 现在这里成了inner1()
            print('wrapper2 ,after func')  # 5
        return inner2
                                
    @wrapper2  #  f = wrapper2(f)  里面的f==inner1  外面的f == inner2
    @wrapper1  # f = wrapper1(f)   里面的f==函数名f  外面的f == inner1
    # 即inner2 = wrapper2(wrapper1(f)),先执行里面的wrapper1(f),return成了inner1,即inner2 = wrapper2(inner1),
    执行wrapper2(inner1),return成了inner2,执行inner2函数,下来在执行inner1函数,在执行f()函数
    def f(): # 3 print('in f') f()

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

    # 优先执行靠近装饰函数的语法糖,即wrapper1

     整个语法糖自上而下,先执行外层的wrapper2,wrapper1,

     在执行wrapper1下的函数体,再执行里层的wrapper1,wrapper2

    7.2.4 带参数的装饰器

    def outer(flag):   # outer(True)
        def timer(func):  # timer(func)
            def inner(*args, **kwargs):
                if flag:
                    print('''执行函数之前要做的''')
                re = func(*args, **kwargs)     
                if flag:
                    print('''执行函数之后要做的''')
                return re
            return inner 
        return timer      
    
    @outer(True)  # func = outer(True)(func)
    def func():
        print(111)
    
    func()
    
    # 这里装饰器所带参数为True,如果把它改为false则装饰器不起装饰作用,相当于去除了装饰功能。

    7.2.5通用装饰公式

    --------------------------记住了,这是固定套路---------------------------------
    # 通用装饰器 def wrapper(func): def inner(
    *args, **kwargs): # 传入万能参数 '''执行函数前的操作''' ret = func(*args, **kwargs) '''执行函数后的操作''' return ret # 将函数的值返回 return inner # 添加装饰器 @wrapper def func(): print('通用装饰器') # 调用函数 func()

     7.3 装饰器相关的扩展知识

    import functools
    
    def wrapper(func):
        @functools.wraps(func)
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    @wrapper
    def f1():
        print('f1')
    
    @ wrapper
    def f2():
        print('f2')
    
    
    print(f1.__name__)      # f1
    print(f2.__name__)      # f2

     关于functools模块的使用,这里由于使用了@functools.wraps(func),本来两者打印的都是inner,但是由于使用了它,输出f1,f2

    作业题

    1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组

    例如:[(‘红心’,2),(‘草花’,2), …(‘黑桃’,‘A’)]

    def cards():
    	s = [(i, j) for i in ['黑', '红', '梅', '方'] for j in range(1, 14)]
    	return s
    
    print(cards())
    
    
    def cards(args):
    	li = []
    	for i in args:
    		for j in range(1, 14):
    			li.append((i, j))
    	print(li)
    cards(['黑', '红', '梅', '方'])
    
    
    标配呀,这个
    def cards(*args):
    	li = []
    	for i in ['黑', '红', '梅', '方']:
    		for j in args:
    			li.append((i, j))
    	return li
    
    print(cards(*(list(range(1, 11))), *['A', 'J', 'Q', 'K']))

    2.写函数,传入n个数,返回字典{‘max’:最大值,’min’:最小值}

    例如:min_max(2,5,7,8,4)

    返回:{‘max’:8,’min’:2}

    def min_max(*args):
    	return dict([('max', max(args)), ('min', min(args))])
    
    print(min_max(515,15,6,13,45))
    

    3.写函数,专门计算图形的面积

    其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积

    调用函数area(‘圆形’,圆半径)  返回圆的面积

    调用函数area(‘正方形’,边长)  返回正方形的面积

    调用函数area(‘长方形’,长,宽)  返回长方形的面积

    def area():

          def 计算长方形面积():

               pass

          def 计算正方形面积():

               pass

          def 计算圆形面积():

               pass

    def area(*args):
    	def inner():
    		if args[0] == '长方形':
    			return args[1]*args[2]
    		if args[0] == '正方形':
    			return args[1]*args[1]
    		if args[0] == '圆形':
    			return args[1]*args[1]*3.14
    	return inner()
    
    print(area('圆形', 3))
    

    4.写函数,传入一个参数n,返回n的阶乘

    例如:cal(7)

    计算7*6*5*4*3*2*1

    def cal(num):
    	s = 1
    	for i in range(num, 0, -1):
    		s = s*i
    	return s
    
    print(cal(7))
    
    
    def cal(num):
    	if num == 1:
    		return 1
    	return num*cal(num-1)
    print(cal(5))
    

    5.给每个函数写一个记录日志的功能,

    功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。

    所需模块:

    import time
    struct_time = time.localtime()
    print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))

    import time
    def func(func2):
    	def inner():
    		struct_time = time.localtime()
    		with open('log.txt', mode='a', encoding='utf-8') as f:
    			f.write('{}	{}
    '.format(func2.__name__,
    					 time.strftime("%Y-%m-%d %H:%M:%S", struct_time)))
    		func2()
    	return inner
    
    @func
    def func1():
    	pass
    func1() 

    6、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码

    dic = {
        'username':None,
        'status':False,
    }
    
    def wrapper(func):
        def inner(*args, **kwargs):
            if dic['status']:
            # 开始是False,所以先走else,用户账户密码正确下,这里改为True,就一直执行这步
                ret = func(*args, **kwargs)
                return ret
            else:
                i = 0
                while i < 3:
                    username = input('请输入用户名:').strip()
                    password = input('请输入密码:').strip()
                    with open('register_msg', encoding='utf-8') as f1:
                        for j in f1:
                            j_li = j.strip().split()  # ['张三','123']
                            if username == j_li[0] and password == j_li[1]:
                                dic['username'] = username
                                dic['status'] = True
                                ret = func(*args, **kwargs)
                                return ret
                        else:   # 注意这里的else缩进,如果在if下面则会循环打印
                            print('账号或者密码错误,请重新输入%s机会' % (2-i))
                            i += 1
    
        return inner
    
    
    @wrapper
    def article():
        print('文章')
    
    @wrapper
    def diary():
        print('日记')
    
    @wrapper
    def comment():
        print('评论')
    
    @wrapper
    def file():
        print('文件')
    
    
    article()
    diary()
    comment()
    file()

    7.在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:

          设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。

          设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。

          循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。

          供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。

    相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。

    # user_pwd文件内容: {'微信':{'username':'老男孩', 'password': '123'},
    #               'qq':{'username':'老男孩1', 'password': '123'},}
    dic = {
        'username':None,
        'status':False,
    }
    
    
    def login(flag):
        def wrapper(func):
            def inner(*args, **kwargs):
                if dic['status']:
                    ret = func(*args, **kwargs)
                    return ret
                else:
                    i = 0
                    while i < 3:
                        username = input('请输入用户名(用%s账号):' % flag).strip()
                        password = input('请输入密码:').strip()
                        with open('user_pwd',encoding='utf-8') as f1:
    				        msg_dic = eval(f1.readline())  # 通过eval函数强制性将字符串转换成了字典
                            # {'微信': {'password': '123', 'username': '老男孩'},
                            # 'qq': {'password': '123', 'username': '老男孩1'}}
                            if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
                                dic['username'] = username
                                dic['status'] = True
                                ret = func(*args, **kwargs)
                                return ret
                            else:
                                print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))
                                i += 1
            return inner
        return wrapper
    
    
    
    
    @login('微信')
    def taobao_home():
        print('淘宝首页')
    
    @login('微信')
    def taobao_shop():
        print('淘宝超市')
    
    @login('qq')
    def jingdong_home():
        print('京东首页')
    
    @login('qq')
    def jingdong_shop():
        print('京东超市')
    
    choice_dict = {
        1: taobao_home,
        2: taobao_shop,
        3: jingdong_home,
        4: jingdong_shop,
    }
    
    while True:
        print('1 淘宝首页
    2 淘宝超市
    3 京东首页
    4 京东超市')
        choice_num = input('请选择输入的序号:').strip()
        if choice_num.isdigit():
            choice_num = int(choice_num)
            if 0 < choice_num <= len(choice_dict):
                choice_dict[choice_num]()
            else:
                print('请输入范围内的序号')
        else:
            print('您输入的有非法字符,请重新输入')
    

      

       

  • 相关阅读:
    django 单表操作
    爬虫学习
    DRF源码分析
    DRF权限和频率限制
    DRF版本和认证源码分析
    request高级部分
    多任务异步爬虫及selenium模块使用
    requests基本使用
    爬虫介绍及环境
    Flask生命周期的源码流程
  • 原文地址:https://www.cnblogs.com/LearningOnline/p/8457804.html
Copyright © 2020-2023  润新知