函数值之装饰器

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('{}\t{}\n'.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 淘宝首页\n2 淘宝超市\n3 京东首页\n4 京东超市')
    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('您输入的有非法字符,请重新输入')

  

   

posted @ 2018-02-21 23:44  -Learning-  阅读(265)  评论(0编辑  收藏  举报