總目錄:http://www.itdecent.cn/p/e406a9bc93a9
Python - 子目錄:http://www.itdecent.cn/p/50b432cb9460
裝飾器三前提:作用域、高階函數(shù),閉包
作用域
?L_E_G_B
a = 10? ?
def f():
? ? a=5? ?
? ? def inner():
? ? ? ? a = 7
? ? ? ? print(a)
? ? ? ? return 1
7
高階函數(shù)
1.函數(shù)名可以作為參數(shù)輸入
2.也可以作為返回值
閉包
def outer():
? ? x = 10
? ? def inner():? ? ? #條件1:?inner就是內(nèi)部變量
? ? ? ? print(x)? ? ? ? #條件2:外部環(huán)境的一個(gè)變量
? ? return inner? ? ?#結(jié)論:內(nèi)部函數(shù)inner是一個(gè)閉包
f=outer()
f()
10
裝飾器
裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。裝飾器是解決這類問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。
現(xiàn)在我們來(lái)舉一個(gè)例子:
假設(shè)要寫一個(gè)銀行存款取款的程序,那么主干程序肯定要實(shí)現(xiàn)存款功能和取款功能:
def a():
? ? print("存款中……")
def b():
? ? print("取款中……")
button=1
if button ==1:
? ? a()
else:
? ? b()
但是除了存款功能和取款功能外,需要添加密碼驗(yàn)證功能,那么菜雞程序員肯定會(huì)添加一個(gè)新的函數(shù):
def a():
? ? print("存款中……")
def b():
? ? print("取款中……")
def c():
? ? print("密碼驗(yàn)證中……")
button=1
if button ==1:
? ? c()
? ? a()
else:
? ? c()
? ? b()
或者:
def a():
? ? c()
? ? print("存款中……")
def b():
? ? c()
? ? print("取款中……")
def c():
? ? print("密碼驗(yàn)證中……")
button=1
if button ==1:
? ? a()
else:
? ? b()
但是這兩種寫法冗余程度高,都違背了開(kāi)放封閉原則,這只是兩個(gè)功能就要每一步都要做出修改,那么上百個(gè)功能呢?
那么我們?cè)诓桓淖冊(cè)瘮?shù)的情況下進(jìn)行修改:
def a():
? ? print("存款中……")
def b():
? ? print("取款中……")
def c(fun):
? ? print("密碼驗(yàn)證中……")
? ? fun()
button=1
if button ==1:
? ? c(a)
else:
? ? c(b)
這樣修改確實(shí)沒(méi)有修改原代碼,但是程序邏輯已經(jīng)改變,如果功能多的話,都要一一修改,可維護(hù)性差。
這樣,就需要我們使用裝飾器了,在不影響原程序,原邏輯,不違背開(kāi)放封閉原則的情況下:
def c(func):
? ? def inner():
? ? ? ? print("密碼驗(yàn)證中……")
? ? ? ? func()
? ? return inner
def a():
? ? print("存款中……")
def b():
? ? print("取款中……")
button=1
if button ==1:
? ? a1=c(a)
? ? a1()
else:
? ? b1=c(b)
? ? b1()
這個(gè)例子只是在說(shuō)明裝飾器的用途和標(biāo)準(zhǔn)用法。
帶參裝飾器
現(xiàn)在讓我們擺脫這個(gè)例子,看一下帶參數(shù)的裝飾器,
def c(func):
? ? def inner():
? ? ? ? print("woshi")
? ? ? ? func()
? ? return inner
def my(a):
? ? print(a)
my1=c(my("xiaobai"))
my1()
運(yùn)行這個(gè)代碼,會(huì)報(bào)錯(cuò),原因是裝飾器的返回值是inner,而my等同于inner。
現(xiàn)在我們修改一下
def c(func):
? ? def inner(*a,**b):
? ? ? ? print("woshi")
? ? ? ? func(*a,**b)
? ? return inner
def my(a):
? ? print(a)
my1=c(my("xiaobai"))
my1()
現(xiàn)在我們來(lái)看一下,裝飾器中的函數(shù)返回值:
def c(func):
? ? def inner(*a,**b):
? ? ? ? print("woshi")
? ? ? ? func(*a,**b)
? ? return inner
@c
def my1(a):
? ? return a
@c
def my2(a):
? ? print(a)
re1 = my1("111")
re2 = my2("222")
print(re1,re2)
woshi
woshi
222
None None
這是打印的結(jié)果,可以看出來(lái)兩個(gè)函數(shù)的返回值均為空,是因?yàn)?,無(wú)論被裝飾的函數(shù)有無(wú)返回值,其結(jié)果都無(wú)返回值,原因其實(shí)很簡(jiǎn)單,因?yàn)閕nner()函數(shù)根本就沒(méi)有返回值。為了實(shí)現(xiàn)有返回值的函數(shù)被裝飾之后仍然有返回值,需要inner函數(shù)與被裝飾函數(shù)的返回值保持一致。
再來(lái)簡(jiǎn)單修改一下:
def c(func):
? ? def inner(*a,**b):
? ? ? ? print("woshi")
? ? ? ? re3=func(*a,**b)
? ? ? ? return re3
? ? return inner
@c
def my1(a):
? ? return a
@c
def my2(a):
? ? print(a)
re1 = my1("111")
re2 = my2("222")
print(re1,re2)
woshi
woshi
222
111 None
可以看到,有返回值的函數(shù)被裝飾之后依然有返回值,沒(méi)有返回值的函數(shù)被裝飾之后則沒(méi)有返回值,符合我們想要的結(jié)果。
語(yǔ)法糖
上面的@c便是語(yǔ)法糖。
我們來(lái)定義一個(gè)函數(shù)
def my():
? ? print("123")
然后我們要在123上加一行=和一行*
def a(func):
? ? def inner():
? ? ? ? print('='*15)
? ? ? ? func()
? ? return inner
def b(func):
? ? def inner():
? ? ? ? print('*'*15)
? ? ? ? func()
? ? return inner
@a
@b
def my():
? ? print("123")
my()
帶參語(yǔ)法糖
但是這樣的話,函數(shù)a和函數(shù)b代碼冗余,可以用帶參數(shù)的語(yǔ)法糖來(lái)優(yōu)化一下:
def a(char):
? ? def b(func):
? ? ? ? def inner():
? ? ? ? ? ? print(char*15)
? ? ? ? ? ? func()
? ? ? ? return inner
? ? return b
@a('=')
@a('*')
def my():
? ? print("123")
my()