先看一個例子:python
#定義一個函數
def test(number): #在函數內部在定義一個函數,而且這個函數用到外圍函數的變量
#那麼將這個函數及用到的一些變量稱之爲閉包
def test_in(number_in): print("在test_in函數內部,number_in的值爲:%d"%number_in) return number+number_in #其實這裏返回的是閉包,也就是內部的函數引用
return test_in #給test函數賦值,這個20就是參數number
ret = test(20) #注意這裏的100就是參數number_in
print(ret(100))
運行結果爲:redis
在test_in函數內部,number_in的值爲:100
120
說明:數據庫
裝飾器是程序開發中常常會⽤到的⼀個功能,因此這也是Python⾯試中必問的問題。閉包
定義:app
知識點:函數
典型結構:spa
def func(args): def func_in(args_in): pass
return func_in
一、先看一個例子code
某公司有多個研發部⻔,1個基礎平臺部⻔,基礎平臺負責提供底層的功能,如:數據庫操做、redis調⽤、監控API等功能。研發部⻔使⽤基礎功能時,只需調⽤基礎平臺提供的功能便可。以下:對象
--------------基礎平臺提供的功能--------------
def func1(): pass
def func2(): pass
def func3(): pass
--------------研發部門A使用基礎平臺-------------- func1() func2() func3() --------------研發部門B使用基礎平臺-------------- func1() func2() func3()
隨着項目進度的深刻,產品經理提出,要在基礎平臺的提供的全部功能中,添加驗證功能,不能誰均可以使用基礎平臺的所有功能,即執行功能前,先進行驗證。blog
項目經理將此功能交給了小A去實現。
小A就去和每一個研發部溝通,讓每一個研發部本身把驗證的代碼加上,結果次日就被辭職了。
項目經理又將此功能交給了小B去實現。
小B吸收小A的經驗,開始本身改代碼:
--------------基礎平臺提供的功能--------------
def func1(): #驗證1
#驗證2
pass
def func2(): #驗證1
#驗證2
pass
def func3(): #驗證1
#驗證2
pass
--------------研發部門A使用基礎平臺-------------- func1() func2() func3() --------------研發部門B使用基礎平臺-------------- func1() func2() func3()
沒過多久小B也被開除了。。。
項目經理又把工做交給了小C,小C對基礎平臺代碼進行重構,其餘業務部門無需作任何修改
--------------基礎平臺提供的功能--------------
def check_login(): #驗證1
#驗證2
pass
def func1(): check_login() pass
def func2(): check_login() pass
def func3(): check_login() pass
--------------研發部門A使用基礎平臺-------------- func1() func2() func3() --------------研發部門B使用基礎平臺-------------- func1() func2() func3()
項目經理看後表示還不錯,可是感受仍是差了一點點,因而決定再也不低調,不再讓小弟作了,因而本身作了一個方案:
--------------基礎平臺提供的功能--------------
def check_login(func): def inner(): #驗證1
#驗證2
func() return inner @check_login def func1(): pass @check_login def func2(): pass @check_login def func3(): pass
--------------研發部門A使用基礎平臺-------------- func1() func2() func3() --------------研發部門B使用基礎平臺-------------- func1() func2() func3()
對於上述代碼,也是僅僅對基礎平臺的代碼進⾏修改,就能夠實如今其餘⼈調⽤函數 func1(), func2(), func3()以前都進⾏【驗證】操做,而且其餘研發部⻔⽆需作任何操做。
單獨以func1()爲例講解:
def check_login(func): def inner(): #驗證1
#驗證2
func() return inner @check_login def func1(): pass
python解釋器就會從上到下解釋代碼,步驟以下:
1 def check_login(func): ==>將check_login函數加載到內存 2 @check_login
沒錯, 從表⾯上看解釋器僅僅會解釋這兩句代碼,由於函數在沒有被調⽤以前其內部代碼不會被執⾏。從表⾯上看解釋器着實會執⾏這兩句,可是 @check_login這⼀句代碼⾥卻有⼤⽂章, @函數名 是python的⼀種語法糖
上例@check_login內部會執⾏⼀下操做:
執行check_login函數,並將@check_login下面的函數做爲check_login函數的參數,
即@check_login等價於check_login(func1),因此內部就會去執行:
def check_login(func): def inner(): #驗證1
#驗證2
func() #func是參數。此時的func就是函數func1()
#返回inner,inner的內部就是執行func1()函數,可是執行func1()函數前,進行了驗證1,驗證2
return inner
check_login() 的返回值
將執行完的chenk_login函數返回值賦值 給@check_login下面的函數的函數名func1 即將check_login()的返回值再從新賦值給func1,即:
新func1 = def inner(): #驗證1
#驗證2
func() #func是參數。此時的func就是函數func1()
#返回inner,inner的內部就是執行func1()函數,可是執行func1()函數前,進行了驗證1,驗證2
return inner
因此,之後研發部門想要執行func1函數時,就會執行新func1函數,在新func1函數內部先執行驗證,再執行原來的func1函數,而後將原來func1函數的返回值返回給了業務調用者。
#定義一個裝飾器:實現加粗效果
def makeBold(fn): def wrapped(): return "<b>"+fn()+"</b>"
return wrapped #定義一個裝飾器:實現斜體效果
def makeItalic(fn): def wrapped(): return "<i>"+fn()+"</i>"
return wrapped #使用裝飾器裝飾函數
@makeBold def test(): return "Hello World"
#使用裝飾器裝飾函數
@makeItalic def test1(): return "Hello World" @makeBold @makeItalic def test2(): return "Hello World"
print(test()) print(test1()) print(test2())
運行結果爲:
<b>Hello World</b>
<i>Hello World</i>
<b><i>Hello World</i></b>
例1:⽆參數的函數
def test_out(func): def test_in(): print("name-%s"%func.__name__) func() return test_in @test_out def test(): pass test()
運行結果爲:name-test
例2:被裝飾的函數有參數
def test_out(func): def test_in(a,b): print(a,b) func(a,b) return test_in @test_out def test(a,b): print("a+b=",a+b) test(1,2)
運行結果爲:
1 2 a+b= 3
例3:被裝飾的函數有不定⻓參數
def test_out(func): def test_in(*args,**kwargs): func(*args,**kwargs) return test_in @test_out def test(*args,**kwargs): print(args,kwargs) test(1) test(1,2) test(1,2,3,k="v")
運行結果爲:
(1,) {} (1, 2) {} (1, 2, 3) {'k': 'v'}
說明:若是被修飾的函數有參數,則裝飾器內部的函數也要有一樣個數的參數才能夠匹配成功。
例4:裝飾器中的return
def test_out(func): def test_in(): func() return test_in @test_out def test(): return "hello" result = test() print(result)
運行結果爲:None
若是修改裝飾器爲 return func() ,則運⾏結果:
def test_out(func): def test_in(): return func() return test_in @test_out def test(): return "hello" result = test() print(result)
運行結果爲:hello
⼀般狀況下爲了讓裝飾器更通⽤,能夠有return
例5:裝飾器帶參數,在原有裝飾器的基礎上,設置外部變量
裝飾器函數實際上是⼀個接⼝約束,它必須接受⼀個callable對象做爲參數,而後返回⼀個callable對象。在Python中⼀般callable對象都是函數,但也有例外。只要某個對象重寫了 __call__() ⽅法,那麼這個對象就是callable
class Test(): def __call__(self): print("call me") t = Test() t()
執行結果:call me
類裝飾器demo
class Test(): def __init__(self,func): print("----初始化----") print("func name is %s"%func.__name__) self.__func = func def __call__(self): print("----類裝飾器的功能----") self.__func() print("----類裝飾器執行完畢----") @Test def test(): print("----test----") test()
運行結果爲:
----初始化---- func name is test ----類裝飾器的功能----
----test----
----類裝飾器執行完畢----
說明: