日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
裝飾器為什么難以理解?

無論項目中還是面試都離不開裝飾器話題,裝飾器的強(qiáng)大在于它能夠在不修改原有業(yè)務(wù)邏輯的情況下對代碼進(jìn)行擴(kuò)展,權(quán)限校驗、用戶認(rèn)證、日志記錄、性能測試、事務(wù)處理、緩存等都是裝飾器的***應(yīng)用場景,它能夠***程度地對代碼進(jìn)行復(fù)用。

但為什么初學(xué)者對裝飾器的理解如此困難,我認(rèn)為本質(zhì)上是對Python函數(shù)理解不到位,因為裝飾器本質(zhì)上還是函數(shù)。

函數(shù)的定義

理解裝飾器前,需要明白函數(shù)的工作原理,我們先從一個最簡單函數(shù)定義開始:

 
 
 
 
  1. def foo(num): 
  2.     return num + 1 

上面定義了一個函數(shù),名字叫foo,也可以把 foo 可理解為變量名,該變量指向一個函數(shù)對象

調(diào)用函數(shù)只需要給函數(shù)名加上括號并傳遞必要的參數(shù)(如果函數(shù)定義的時候有參數(shù)的話)

 
 
 
 
  1. value = foo(3) 
  2. print(value) # 4 

變量名 foo 現(xiàn)在指向 函數(shù)對象,但它也可以指向另外一個函數(shù)。

 
 
 
 
  1. def bar(): 
  2.     print("bar") 
  3. foo = bar 
  4. foo() # bar 

函數(shù)作為返回值

在Python中,一切皆為對象,函數(shù)也不例外,它可以像整數(shù)一樣作為其它函數(shù)的返回值,例如:

 
 
 
 
  1. def foo(): 
  2.     return 1 
  3.  
  4. def bar(): 
  5.     return foo 
  6.  
  7. print(bar()) #  
  8.  
  9. print(bar()()) # 1  
  10. # 等價于 
  11. print(foo()) # 1 

bar() 的返回值是一個函數(shù)對象,所以我們可以繼續(xù)對返回值進(jìn)行調(diào)用,調(diào)用bar()()等價于調(diào)用 foo(),因為 變量 foo 指向的對象與 bar() 的返回值是同一個對象。

函數(shù)作為參數(shù)

函數(shù)還可以像整數(shù)一樣作為函數(shù)的參數(shù),例如:

 
 
 
 
  1. def foo(num): 
  2.     return num + 1 
  3.  
  4. def bar(fun): 
  5.     return fun(3) 
  6.  
  7. value = bar(foo) 
  8. print(value)  # 4 

函數(shù) bar 接收一個參數(shù),這個參數(shù)是一個可被調(diào)用的函數(shù)對象,把函數(shù) foo 傳遞到 bar 中去時,foo 和 fun 兩個變量名指向的都是同一個函數(shù)對象 。所以調(diào)用 fun(3) 相當(dāng)于調(diào)用 foo(3)。

函數(shù)嵌套

函數(shù)不僅可以作為參數(shù)和返回值,函數(shù)還可以定義在另一個函數(shù)中,作為嵌套函數(shù)存在,例如:

 
 
 
 
  1. def outer(): 
  2.     x = 1 
  3.     def inner(): 
  4.         print(x) 
  5.     inner() 
  6.  
  7. outer() # 1 

inner做為嵌套函數(shù),它可以訪問外部函數(shù)的變量,調(diào)用 outer 函數(shù)時,發(fā)生了3件事:

  • 給 變量 x 賦值為1
  • 定義嵌套函數(shù) inner,此時并不會執(zhí)行 inner 中的代碼,因為該函數(shù)還沒被調(diào)用,直到第3步
  • 調(diào)用 inner 函數(shù),執(zhí)行 inner 中的代碼邏輯。

閉包

再來看一個例子:

 
 
 
 
  1. def outer(x): 
  2.     def inner(): 
  3.         print(x) 
  4.  
  5.     return inner 
  6. closure = outer(1) 
  7. closure() # 1 

同樣是嵌套函數(shù),只是稍改動一下,把局部變量 x 作為參數(shù)了傳遞進(jìn)來,嵌套函數(shù)不再直接在函數(shù)里被調(diào)用,而是作為返回值返回,這里的 closure就是一個閉包,本質(zhì)上它還是函數(shù),閉包是引用了自由變量(x)的函數(shù)(inner)。

裝飾器

繼續(xù)往下看:

 
 
 
 
  1. def foo(): 
  2.     print("foo") 

上面這個函數(shù)這可能是史上最簡單的業(yè)務(wù)代碼了,雖然沒什么用,但是能說明問題就行?,F(xiàn)在,有一個新的需求,需要在執(zhí)行該函數(shù)時加上日志:

 
 
 
 
  1. def foo(): 
  2.     print("記錄日志開始") 
  3.     print("foo") 
  4.     print("記錄日志結(jié)束") 

功能實現(xiàn),唯一的問題就是它需要侵入到原來的代碼里面,把日志邏輯加上去,如果還有好幾十個這樣的函數(shù)要加日志,也必須這樣做,顯然,這樣的代碼一點都不Pythonic。那么有沒有可能在不修改業(yè)務(wù)代碼的提前下,實現(xiàn)日志功能呢?答案就是裝飾器。

 
 
 
 
  1. def outer(func): 
  2.     def inner(): 
  3.         print("記錄日志開始") 
  4.         func() # 業(yè)務(wù)函數(shù) 
  5.         print("記錄日志結(jié)束") 
  6.     return inner 
  7.  
  8. def foo(): 
  9.     print("foo") 
  10.  
  11. foo = outer(foo)  
  12. foo() 

我沒有修改 foo 函數(shù)里面的任何邏輯,只是給 foo 變量重新賦值了,指向了一個新的函數(shù)對象。***調(diào)用 foo(),不僅能打印日志,業(yè)務(wù)邏輯也執(zhí)行完了?,F(xiàn)在來分析一下它的執(zhí)行流程。

這里的 outer 函數(shù)其實就是一個裝飾器,裝飾器是一個帶有函數(shù)作為參數(shù)并返回一個新函數(shù)的閉包,本質(zhì)上裝飾器也是函數(shù)。outer 函數(shù)的返回值是 inner 函數(shù),在 inner 函數(shù)中,除了執(zhí)行日志操作,還有業(yè)務(wù)代碼,該函數(shù)重新賦值給 foo 變量后,調(diào)用 foo() 就相當(dāng)于調(diào)用 inner()

foo 重新賦值前:

重新賦值后:

另外,Python為裝飾器提供了語法糖 @,它用在函數(shù)的定義處:

 
 
 
 
  1. @outer 
  2. def foo(): 
  3.     print("foo") 
  4.  
  5. foo() 

這樣就省去了手動給foo重新賦值的步驟。

到這里不知你對裝飾器理解了沒有?當(dāng)然,裝飾器還可以更加復(fù)雜,比如可以接受參數(shù)的裝飾器,基于類的裝飾器等等。

【本文是專欄作者“劉志軍”的原創(chuàng)文章,作者微信公眾號:Python之禪(VTtalk)】


文章標(biāo)題:裝飾器為什么難以理解?
網(wǎng)站URL:http://m.5511xx.com/article/dhoesoi.html