一,装饰器的概念
器即函数
装饰即修饰,顾名思义就是为其他函数添加新功能
装饰器的定义:装饰器本质上就是一个函数,这个函数接收其他函数做为参数,并将以一个新的修改后的函数进行替换。装饰器的应用场景就是对
多个函数提供在其之前,之后或周围进行调用的通用代码
二,装饰器需要遵循的原则
1,不修改被装饰函数的源代码(开放封闭原则)
2,为被装饰函数添加新功能后,不修改被装饰器的调用方式
三,上面刚刚有写到装饰器本身就是函数,只是这个函数的功能比普通函数的功能要强大很多,那么一个函数怎样才能是一个装饰器呢
装饰器=高阶函数+函数嵌套+闭包
以前在博客中有写过什么是高阶函数,函数嵌套,现在我们在来回顾一下
3.1高阶函数的定义
3.1.1函数接收的参数是一个函数名
3.1.2函数的返回值是一个函数名
3.1.3满足以上条件任意一个,都可以称之为高阶函数
1 def foo(): 2 print('我的函数名做为高阶函数的参数,传给高阶函数') 3 4 def test_one(func): 5 print('我是高阶函数1,我接收的参数是%s'%func) 6 func() 7 8 def test_two(func): 9 print('我是高阶函数2,我的返回值是%s' %func) 10 return func 11 12 test_one(foo) 13 ######执行结果######### 14 #我是高阶函数1,我接收的参数是<function foo at 0x0000000001140048> 15 #我的函数名做为高阶函数的参数,传给高阶函数 16 17 test_two(foo) 18 ######执行结果######### 19 #我是高阶函数2,我的返回值是<function foo at 0x00000000006F0048>
1 #高阶函数应用1:把函数名当做参数传给高阶函数 2 #我们来计算一下foo这个函数执行花了多长时间 3 4 import time 5 def foo(): 6 time.sleep(1) 7 print('我是函数foo,我的运行时间是多久') 8 9 def test(func): 10 ''' 11 计算foo这个函数运行时间 12 :param func: 将foo这个函数作为参数,计算运行时间 13 :return: 14 15 ''' 16 start_time = time.time() 17 func() 18 stop_time = time.time() 19 print('函数foo的运行时间是%s' % (stop_time - start_time)) 20 21 test(foo) 22 23 #########执行结果####### 24 #我是函数foo,我的运行时间是多久 25 #函数foo的运行时间是1.0000572204589844 26 27 #总结:在这个示例中,我们虽然为foo增加了计算运行时间的功能,但是foo原来的执行 28 方式是foo(),现在我们需要调用高阶函数test(foo),改变了函数的调用方式
1 #高阶函数应用2:把函数名当作参数传给高阶函数,高阶函数直接返回函数名 2 3 import time 4 def foo(): 5 time.sleep(1) 6 print('我是函数foo,我的运行时间是多久') 7 8 def test(func): 9 ''' 10 计算foo这个函数运行时间 11 :param func: 将foo这个函数作为参数,计算运行时间 12 :return: 13 14 ''' 15 start_time = time.time() 16 return func 17 stop_time = time.time() 18 print('函数%s的运行时间是%s' % (func,stop_time - start_time)) 19 20 foo = test(foo) 21 foo() 22 23 ######执行结果######## 24 #我是函数foo,我的运行时间是多久 25 26 27 #总结:这次执行我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
高阶函数总结:
1,函数接收的参数是一个函数名
作用:在不修改函数源代码的前提下,为函数添加新功能
不足:会改变函数的调用方式
2,函数的返回值是一个函数名
作用:不修改函数的调用方式
不足:不能添加新功能
3.2 函数嵌套
1 user_name = 'jack' 2 def test_one(name): 3 print('my name is %s' %name) 4 def test_two(): 5 name = 'zcy' 6 print('test-two:%s' %name) 7 def test_three(): 8 print('test_three',name) 9 test_three() 10 test_two() 11 test_one(user_name) 12 13 #########执行结果###### 14 #my name is jack 15 #test-two:zcy 16 #test_three zcy
3.3闭包
1 def test_one(name): 2 ''' 3 闭包:在一个作用域里放入定义变量,相当于打了一个包 4 :param name: 5 :return: 6 ''' 7 print('my name is %s' %name) 8 def test_two(): 9 name = 'zcy' 10 print('test-two:%s' %name) 11 def test_three(): 12 print('test_three',name) 13 test_three() 14 test_two() 15 test_one('laiying') 16 17 #########执行结果######### 18 #my name is laiying 19 #test-two:zcy 20 #test_three zcy
四.无参装饰器
无参装饰器=高阶函数+函数嵌套
接下来我们开始创建装饰器
我们先来创建一个最简单的装饰器,
1 def test(func): 2 return func 3 4 @test 5 def foo(name): 6 print('My name is %s' %name) 7 foo('laiying') 8 9 #它和下面的过程类似 10 11 def test(func): 12 return func 13 14 def foo(name): 15 print('My name is %s' % name) 16 foo = test(foo) 17 18 #这个装饰器没什么用,但是可以正常运行,只不过它什么都不做
语法糖@
1 @timer #@timer就等同于cal=timer(cal) 2 def cal(array): 3 res=0 4 for i in array: 5 res+=i 6 return res 7 8 cal(range(10))
接下来我们有一个要求,比如视频刚上线初期,为了吸引用户,你们采取了免费政策,所有视频免费观看,迅速吸引了一大批用户,免费一段时间后,每天巨大的带宽费用公司承受不了了,所以准备对比较受欢迎的几个版块收费,其中包括“欧美” 和 “日韩”专区。
接下来要这样才能实现这个要求呢
思路:想收费得先让其进行用户认证,认证通过后,再判定这个用户是否是VIP付费会员就可以了,是VIP就让看,不是VIP就不让看就行了
注意事项:在我们要修改代码前,一定要遵循软件开发原则中的一个“开发-封闭”原则,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
封闭:已实现的功能代码块
开放:对扩展开发
user_dic = [ {'name':'laiying','pwd':123}, {'name':'zcy','pwd':123}, {'name':'alex','pwd':123}, {'name':'jack','pwd':123}, ] login_list = {'username':None,'login':False}
username = input('请输入您的用户名:')
paswd = int(input('请输入您的密码:')) def foo(func): # func = jd_index def test_one(*args,**kwargs): # jd_index(name) args='laiying' if login_list['username'] and login_list['login']: res = func(*args,**kwargs) # 4,func是foo的一个形式参数,这个形式参数对应的实际参数是jd_index这个函数 #所以现在所执行就是在调用jd_index这个函数 return res #将jd_index这个函数的执行结果返回给test_one,谁调用就返回给谁 for i in user_dic: if username == i['name'] and paswd == i['pwd']: login_list['username'] = username login_list['login'] = True print('恭喜您登录成功') result = func(*args,**kwargs) # 与(4)同理,运行jd_index return result else: print('您的用户名或者密码错误,请重新输入') return test_one # 2 将test_one这个函数名retrun给 foo
@foo # 1. 开始调用foo这个装饰器,并将调用foo装饰器的这个函数(jd_index),做为参数传给foo # jd_index = foo(jd_index) def jd_index(name): print('hi %s! 您好,欢迎您登录xx首页'%name) return '执行成功。哈哈哈哈哈哈哈' # 5 ,这个return的结果是jd_index这个函数的一个返回值 jd_index(username) # 3,执行jd_index()就等于在执行foo()(),因为在第一步@foo的时候jd_index = foo(jd_index),相当于 #foo这个函数的执行结果从新赋值给了jd_index,所以现在执行jd_index,其实在执行foo这个函数的一个执行结果,而这个 #foo的执行就是return test_one 这个返回的test_one函数的内存地址,所以现在执行的就是test_one这个函数的内存地址, #就是在执行test_one这个函数了 @foo def america(): print('----欧美专区----') america() @foo def japan(): print('----日韩专区----') japan()
五,有参装饰器
1 user_dic = [ 2 {'name':'laiying','pwd':123}, 3 {'name':'zcy','pwd':123}, 4 {'name':'alex','pwd':123}, 5 {'name':'jack','pwd':123}, 6 ] 7 login_list = {'username':None,'login':False} 8 username = input('请输入您的用户名:') 9 paswd = int(input('请输入您的密码:')) 10 def auth(auth_type='file'): 11 def foo(func): # func = id_index 12 def test_one(*args,**kwargs): 13 if auth_type == 'file': 14 if login_list['username'] and login_list['login']: 15 res = func(*args,**kwargs) 16 17 return res 18 19 for i in user_dic: 20 if username == i['name'] and paswd == i['pwd']: 21 login_list['username'] = username 22 login_list['login'] = True 23 print('恭喜您登录成功') 24 result = func(*args,**kwargs) 25 return result 26 else: 27 print('您的用户名或者密码错误,请重新输入') 28 elif auth_type == 'ldap': 29 print('你现在选择的类型是日韩板块。。。') 30 result = func(*args,**kwargs) 31 return result 32 else: 33 print('欢迎您选择大陆板块') 34 result = func(*args, **kwargs) 35 return result 36 37 return test_one 38 return foo
#auth(auth_type='file')就是在运行一个函数,然后返回foo,所以@auth(auth_type='file')
#就相当于@foo,只不过现在,我们的foo作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
39 @auth() 41 def jd_index(name): 42 print('hi %s! 您好,欢迎您登录xx首页'%name) 43 return '执行成功。哈哈哈哈哈哈哈' 44 jd_index(username) 45 49 50 @auth(auth_type='file') 51 def america(): 52 print('----欧美专区----') 53 54 america() 55 @auth(auth_type='ldap') 56 def japan(): 57 print('----日韩专区----') 58 japan()