裝飾器:本質是函數,用於裝飾其他函數,在不改變其他函數的調用和代碼的前提下,增加新功能
原則:
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('\033[32;1m user has passed authentication \033[0m') res = func(*args,**kwargs) print('xxxxx') return res else: exit('\033[31;1m invalid username or password \033[0m') elif auth_type == 'ldap': print('xxxx') return wrapper return outer_wrapper # @auth def index(): print('welcome to index page') @auth(auth_type='local') #home=wrapper() def home(): print('welcom to home page') return ('from home') # 函數結果被改變 @auth(auth_type='ldap') def bbs(): print('welcome to bbs page') index() print(home()) # wrapper() bbs()