python高級-裝飾器(19)

1、什麼是閉包

先看一個例子: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中的閉包能夠

 

2、什麼是裝飾器

裝飾器是程序開發中常常會⽤到的⼀個功能,因此這也是Python⾯試中必問的問題。閉包

定義:app

  1. 裝飾器自己就是一個函數
  2. 爲其餘函數提供附加功能
  3. 不改變被修飾函數的源代碼
  4. 不改變原調用方式
  5. 裝飾器=高階函數+嵌套函數

知識點:函數

  • 函數自己就是一個變量(意味着能夠被複制給一個變量:test=test(1) )
  • 高階函數:把函數名當成一個實參傳遞給另外一個函數func(test1) (不改變源代碼的前提下添加代碼)
  • 返回值中包含函數名return deco (不改變函數的調用方式)
  • 嵌套函數:函數中加入新的函數

典型結構:spa

def func(args): def func_in(args_in): pass
     return func_in

 

3、裝飾器案例

一、先看一個例子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函數的返回值返回給了業務調用者。

 

4、裝飾器應用

#定義一個裝飾器:實現加粗效果
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:裝飾器帶參數,在原有裝飾器的基礎上,設置外部變量

 

 

6、類裝飾器

裝飾器函數實際上是⼀個接⼝約束,它必須接受⼀個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----
----類裝飾器執行完畢----

說明:

  • 當用Test來裝做裝飾器對test函數進行裝飾的時候,首先會建立Test的實例對象
  • 而且會把test這個函數名當作參數傳遞到__init__方法中,即在__init__方法中的func變量執行了test函數體
  • test函數至關於指向了用Test建立出來的實例對象
  • 擋在使用test()進行調用時,就至關於讓這個對象(),所以會調用這個對象的__call__方法
  • 爲了可以在__call__方法中調用原來的test指向的函數體,因此在__init__方法中就須要一個實例屬性保存這個引用,因此纔有了self.__func = func這句代碼,從而在調用__call__方法中就能調用test
相關文章
相關標籤/搜索