装饰器:本质是函数,用于装饰其他函数,在不改变其他函数的调用和代码的前提下,增加新功能
原则:
1.不能修改被装饰函数的源代码
2.不能修改被装饰函数的调用方式
3.装饰函数对于被装饰函数透明
参考如下链接:
http://egon09.blog.51cto.com/9161406/1836763
实现装饰器的需求:
- 函数即“变量”,函数名指向内存中的函数体,加()表示调用
- 高价函数,将函数当做参数传递给其他函数
- 嵌套函数,在函数体内再定义函数
1.调用简单的函数:
import time def test1(): time.sleep(1) print('in the test1') test1() output:
in the test1
2.在不改变原来函数的定义和调用方式,增加计时功能
import time def timmer(func): def warpper(*args,**kwargs): start_time=time.time() func() stop_time=time.time() print('the func run time is %s' %(stop_time-start_time)) return warpper @timmer def test1(): time.sleep(1) print('in the test1') test1()
output:
in the test1
the function run time is -1.0007600784301758:
# /usr/bin/env python # -*- coding: utf-8 -*- # Author:Jenvid.yang # def foo(): # print('in the foo') # bar() # foo() # bar() 未定义,肯定会报错 # def bar(): # print('in the bar') # def foo(): # print('in the foo') # bar() # foo() # def foo(): # print('in the foo') # bar() # def bar(): # print('in the bar') # foo() # 定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义之后即可,各函数间前后关系不影响 # def foo(): # print('in the foo') # bar() # foo() # def bar(): # print('in the bar')
总结1:定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义步骤之后即可,各函数间定义先后不影响,与变量定义先后一个道理
高阶函数
1.把一个函数名当做实参传给另外一个函数--实现装饰器:不修改被装饰函数下,给源代码,也就是被装饰函数增加新功能
2.返回值中包含函数名--实现装饰器:不修改函数的调用方式
- 函数返回实参函数内存地址
def bar():
print('in the bar')
def test1(func):
print(func)
test1(bar)
print('分割线'.center(30,'-'))
def test2(func):
return func
fff=test2(bar) # 将内存地址,也就是函数体,也就是test2返回值,赋值给一个变量
fff() # 变量加上(),表示调用
output:
<function bar at 0x7f5fa0a7d730>
-------------分割线--------------
in the bar
- 高阶函数传参方式实现源代码bar没有被更改,调用方式被更改了
import time def bar(): time.sleep(1) print('in the bar') def test1(func): start_time=time.time() func() stop_time=time.time() print('the function run time is %s' % (stop_time-start_time)) test1(bar)
output:
in the bar
the function run time is 1.0011107921600342
#被调用函数原来调用方式是:bar(),函数体里面的内容也没有更改
#新的函数是test1,调用的是test1(),改变了调用函数名,如果有生产线上已经在调用bar(),这种情况下意味着所有的调用bar函数的其他函数,都需要将bar改成test1
- 高阶函数返回值方式实现函数调用方式不更改
import time
def bar():
time.sleep(1)
print('in the bar')
def test1(func):
start_time=time.time()
return func
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
bar=test1(bar)
bar()
output:
in the bar
# 高阶函数返回内存地址实现调用方式不更改,但函数的新功能又没加上
- 嵌套函数,在高阶函数下,给新功能函数增加多一层函数嵌套
import time def bar(): time.sleep(1) print('in the bar') def timmer(func): def test1(): start_time=time.time() func() stop_time=time.time() print('the function run time is %s' % (stop_time-start_time)) return test1 #返回test1的内存地址 bar=timmer(bar) #将bar函数地址作为实参传递给timmer函数的形参func,func将在嵌套函数test1里面进行调用 bar() #此时的bar其实是timmer函数地址,并不是原来的bar
output:
in the bar
the function run time is 1.0010881423950195
- 语法糖
import time def timmer(func): def test1(): start_time=time.time() func() stop_time=time.time() print('the function run time is %s' % (stop_time-start_time)) return test1 # bar=timmer(bar) @timmer #只需要在被调用函数的前面用关键字符“@装饰器函数”,既可以实现装饰器效果 def bar(): time.sleep(1) print('in the bar') bar()
output:
in the bar
the function run time is 1.0011053085327148
import time def timmer(func): def warpper(): #一般将装饰器嵌套函数名命名为warpper 据说懂了这个可以解决90%的问题 start_time=time.time() func() stop_time=time.time() print("the function run time is %s:" %(start_time-stop_time)) return warpper @timmer #这里还是相当于更改被装饰函数的’变量名‘内存地址 bar=timmer(bar) def bar(): time.sleep(1) print('in the bar') bar()
- 装饰器参数传值-被装饰函数传1个值
import time def timmer(func): def warpper(name): start_time=time.time() func(name) stop_time=time.time() print("the function run time is %s:" %(start_time-stop_time)) return warpper @timmer def test1(name): time.sleep(1) print('in the test1 %s',name) test1('alex')
output:
in the test1 %s alex
the function run time is -1.0006732940673828:
- 装饰器参数传值-被装饰函数传2个值
import time def timmer(func): def warpper(name,age): start_time=time.time() func(name,age) stop_time=time.time() print("the function run time is %s:" %(start_time-stop_time)) return warpper @timmer def test1(name,age): time.sleep(1) print('in the test1 name is %s,age is %d'%(name,age)) test1('alex',18)
output:
in the test1 name is alex,age is 18
the function run time is -1.000108003616333:
- 装饰器参数传值-被装饰函数传n个值
import time def timmer(func): # print('func timmer') def wrapper(*args,**kwargs): print('打印实参:',args,kwargs) start_time=time.time() func(*args,**kwargs) stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return wrapper @timmer # test1=timmer(test1) def test1(name,age): time.sleep(1) print('in the test1 func, name is %s,age is %d' % (name,age)) test1('alex',18) @timmer # test1=timmer(test1) def test2(name,age,sex,salary): time.sleep(1) print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary)) test2('alex',18,'man',3000)
output:
打印实参: ('alex', 18) {}
in the test1 func, name is alex,age is 18
the function run time is 1.0013768672943115:
打印实参: ('alex', 18, 'man', 3000) {}
in the test1 func, name is alex,age is 18,sex is man,salary is 3000
the function run time is 1.0012915134429932:
- 试错过程:
-
import time def timmer(func): def warpper(): # print('打印实参:',args,kwargs) start_time=time.time() func() stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return warpper @timmer #这里相当于test1=timmer(test1)=wrapper,test1加上参数相当于test1(name,age)=wrapper(name,age) def test1(name,age): time.sleep(1) print('in the test1 name is %s,age is %s'%(name,age)) test1('alex',18) 输出如下: /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py Traceback (most recent call last): File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module> test1('alex',18) TypeError: warpper() takes 0 positional arguments but 2 were given Process finished with exit code 1
step1:定义装饰器函数体timmer
step2:语法糖,调用装饰器函数,准备装饰下一行函数,返回内层嵌套函数变量warpper内存地址
step3:定义函数体test1
step4:调用函数test1,将test1作为实参,传递给装饰器timmer
step5:warpper函数调用,需要讲test1的实参传递给wrapper,由于命名空间关系,test1的参数无法传递到warpper,func也无法获得实参 - 给包装函数warrper增加形参
-
import time def timmer(func): def warpper(name,age): # print('打印实参:',args,kwargs) start_time=time.time() func() stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return warpper @timmer #test1=timmer(test1) def test1(name,age): time.sleep(1) print('in the test1 name is %s,age is %s'%(name,age)) test1('alex',18) 输出如下 /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py Traceback (most recent call last): File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module> test1('alex',18) File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 9, in warpper func() TypeError: test1() missing 2 required positional arguments: 'name' and 'age'
这个解释很明显,func就是test1,缺少了2个位置参数,把func()改成func(name,age)即正确
-
# /usr/bin/env python # -*- coding: utf-8 -*- # Author:Jenvid.yang import time def timmer(func): #func=test2 def wrapper(*args,**kwargs): start_time=time.time() func(*args,**kwargs) stop_time=time.time() print('func run time is %s' %(stop_time-start_time)) return wrapper @timmer def bar(): time.sleep(1) print('in the bar') bar() @timmer #test2=timmer(test2)=warpper ->test2(name)=warpper(name) def test2(name): time.sleep(1) print('in the test2',name) test2('alex')
- 被装饰函数如果带有返回值的情况
import time def timmer(func): # print('func timmer') def wrapper(*args,**kwargs): print('打印实参:',args,kwargs) start_time=time.time() func(*args,**kwargs) stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return wrapper @timmer # test1=timmer(test1) def test1(name,age,address): time.sleep(1) print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address)) test1('alex',18,'nuber001') # @timmer # test1=timmer(test1) def test3(name,age,sex,salary): time.sleep(1) # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary)) return (name,age,sex,salary) print(test3('alex',18,'man',3000)) 输出如下: /usr/bin/python3.5 /root/PycharmProjects/S14/day04/decoreate02.py
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function run time is 1.0009291172027588:
('alex', 18, 'man', 3000) Process finished with exit code 0
函数test1,没有返回值,装饰后正常输出
函数test2,带有返回值,没有装饰,打印了实参元组
如果test2装饰后呢?
import time def timmer(func): # print('func timmer') def wrapper(*args,**kwargs): print('打印实参:',args,kwargs) start_time=time.time() func(*args,**kwargs) stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return wrapper @timmer # test1=timmer(test1) def test1(name,age,address): time.sleep(1) print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address)) test1('alex',18,'nuber001') @timmer # test1=timmer(test1) def test3(name,age,sex,salary): time.sleep(1) # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary)) return (name,age,sex,salary) print(test3('alex',18,'man',3000))
输出如下:
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function run time is 1.0010955333709717:
打印实参: ('alex', 18, 'man', 3000) {}
the function run time is 1.0010552406311035:
None
带有返回值的test2被装饰后,变成了None,改变了原函数的输出,这是不允许的
给包装函数中的调用原函数增加赋值和返回值:
import time def timmer(func): # print('func timmer') def wrapper(*args,**kwargs): print('打印实参:',args,kwargs) start_time=time.time() res=func(*args,**kwargs) #用一个变量保存原函数输出值 stop_time=time.time() print("the function run time is %s:" % (stop_time-start_time)) return res #返回原函数结果给wrapper return wrapper @timmer # test1=timmer(test1) def test1(name,age,address): time.sleep(1) print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address)) test1('alex',18,'nuber001') @timmer # test1=timmer(test1) def test3(name,age,sex,salary): time.sleep(1) # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary)) return (name,age,sex,salary) print(test3('alex',18,'man',3000))
输出如下:
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function run time is 1.0010664463043213:
打印实参: ('alex', 18, 'man', 3000) {}
the function run time is 1.0010428428649902:
('alex', 18, 'man', 3000)
- 语法糖带参数情况,index使用本地验证,home使用ldap验证,同一个装饰器,如何实现不同的验证方式?
-
def deco(func): def wrapper(username,password): print('I am 包装1') func(username,password) print('I am 包装2') return wrapper @deco(auth_type='local') def index(username,password): print('welcome to index page',username) index('yzw','secret') @deco(auth_type='ldap') def home(username,password): print('welcome to home page',username) home('alex','secret')
- 再嵌套一层函数
-
def auth(auth_type): def out_wrapper(func): def wrapper(username,password): if auth_type == 'local': print('via local certification') print('I am 包装前') func(username,password) print('I am 包装后') elif auth_type == 'ldap': print('嘻嘻嘻嘻') func(username, password) print('via ldap certfication') return wrapper return out_wrapper @auth(auth_type='local') #1.全部不带参数,相当于index=deco(index) #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args) #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数 def index(username,password): print('welcome to index page <local>',username) index('yzw','secret') @auth(auth_type='ldap') def home(username,password): print('welcome to home page <ldap>',username) home('alex','secret')
输出如下:
/usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decorate04.py
via local certification
I am 包装前
welcome to index page <local> yzw
I am 包装后
嘻嘻嘻嘻
welcome to home page <ldap> alex
via ldap certfication
Process finished with exit code 0 -
#/usr/bin/env python #-*- coding: utf-8 -*- #Author:jenvid.yang def auth(auth_type): def out_wrapper(func): def wrapper(*args,**kwargs): if auth_type == 'local': print('via local certification') func(*args,**kwargs) elif auth_type == 'ldap': print('via ldap certfication') func(*args, **kwargs) return wrapper return out_wrapper @auth(auth_type='local') #1.全部不带参数,相当于index=deco(index) #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args) #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数 def index(username,password): print('welcome to index page <local>',username) index('yzw','secret') @auth(auth_type='ldap') def home(username,password): print('welcome to home page <ldap>',username) home('alex','secret')
-
# /usr/bin/env python # -*- coding: utf-8 -*- # Author:Jenvid.yang import time user,passwd='alex','abc123' def auth(auth_type): print('auth func:',auth_type) def outer_wrapper(func): def wrapper(*args,**kwargs): print('wrapper func args:',*args,**kwargs) if auth_type == 'local': username=input('username:').strip() password=input('password:').strip() if user == username and passwd == password: print('