1. 程式人生 > 程式設計 >通俗易懂了解Python裝飾器原理

通俗易懂了解Python裝飾器原理

作用

裝飾器可以用於用於裝飾一個函式或方法,使得在不修改原函式、方法程式碼的前提下,為方法新增前置或後置操作;

例如突然想要計算一下各個函式的執行時間,又不希望在每一個函式中新增tim.time()來計算執行時間

用法

裝飾器的寫法網上很多,但是我覺得還是儘量先理解,再知道怎麼寫會比較好,所以會先說如何理解,在後面重寫用法

實現

瞭解裝飾器是如何實現的,遠比會寫裝飾器更重要,簡單的說裝飾器就是接收一個函式物件,然後先執行前置操作,再執行函式,再執行後置操作;

這麼說可能有些抽象,或者舉一個不那麼恰當的比較貼近生活的例子;

假設你有一臺像這樣的小風扇:

這颱風扇可以充電,有一個開關,開啟之後扇葉會旋轉,開始工作,當然你也可以插著電開啟開關,也可以充好電之後帶走,在其他地方開啟開關

如果把這個風扇置於一切皆是物件的Python中,風扇就是一個物件,他實現的功能就是出風:

def fengshan():
  return '出風'

為了更好和例子結合,我們用pinyin命名

現在我們實現了一個fengshan函式,返回吹風

如果你稍微有點基礎,你就能知道如何呼叫這個方法

def fengshan():
  return '出風'
print(fengshan())

不要覺得這很基礎很墨跡,如果需要理解裝飾器,你必須知道,呼叫函式的方式是函式名稱加上括號fengshan()
而這個基礎中的基礎中的括號()就是執行函式的開關,如果我們不加括號

def fengshan():
  return '出風'
print(fengshan)

返回的將是一個函式物件(例子中的風扇本身)

<function fengshan at 0x7f8e7c4a6950>

這裡的意思是 一個叫fengshan的funciont,地址在0x7f8e7c4a6950

那現在我們就可以把風扇帶走,在其他地方使用

def fengshan():
  return '出風'
 
func = fengshan
print(fengshan)
print(func)

返回

<function fengshan at 0x7f570eaf3950>
<function fengshan at 0x7f570eaf3950>

這說明func和fengshan是等價的,他們在同一塊記憶體中,所以當我們執行func() 也等價於執行fengshan

def fengshan():
  return '出風'
 
func = fengshan
print('下面是執行fengshan')
print(fengshan())
print('下面是執行func')
print(func())

返回

下面是執行fengshan

出風

下面是執行func

出風

理解到這裡之後你也就能理解裝飾器的實現了,讓我們再看一個例子

def fengshan():
  return '出風'
def wrapper(func):
  return func
print(fengshan)
print(wrapper(fengshan))

這個例子中我們除了保留剛剛一直在用的fengshan函式之外,又定義了一個wrapper

因為python中一切皆是物件,函式也是物件,而函式的入參也可以接收物件,所以函式物件可以作為引數傳遞給另一個函式wrapper

這個wrapper中什麼都沒有做,只是返回了接收的func物件,我們打印出來兩個物件,可以發現他們其實是同一個物件

<function fengshan at 0x7f9b0c92f950>
<function fengshan at 0x7f9b0c92f950>

現在你就已經理解了裝飾器的實現了,而且如果你跟著文中的程式碼敲一遍,你就已經寫了一個裝飾器,你只需要稍加修改,比方說,我們在wrapper內部執行接收的func,並且,在前後加上一些操作

def wrapper(func):
  print('在wrapper中執行func前')
  print(func())
  print('在wrapper中執行func後')
wrapper(fengshan)

返回

在wrapper中執行func前

出風

在wrapper中執行func後

如果你覺得很神奇,無法理解,可以回到風扇的圖片重新再讀一遍,當你理解了上述的程式碼之後,我們就可以加快速度,完成真正的裝飾器的編寫

用法

當你成功明白了函式被定義和呼叫的過程之後,我們開始考慮實際場景,上一段程式碼中我們還是改變了原有函式的呼叫

從呼叫fengshan變成了呼叫func(fengshan)

我們想要實現不改變原有程式碼和呼叫方式的情況下為原有函式新增前置或後置操作,就需要再優化一下我們的裝飾器

def fengshan():
  print('出風')
 
 
def wrapper(func):
  def f():
    print('在wrapper中執行func前')
    func()
    print('在wrapper中執行func後')
  return f
 
fengshan = wrapper(fengshan)
fengshan()

為了看到執行過程,我們把fengshan內部的return改為print

返回

在wrapper中執行func前

出風

在wrapper中執行func後

這裡我們的改變是在wrapper原有的實現中又包了一層方法f,再回想一下我們前面風扇的例子,現在當我們執行wrapper的時候,執行了什麼?

wrapper(func)的返回,是一個函式物件f,這個函式物件的開關沒有被開啟,f中的程式碼不會被執行

我們又用同名的fengshan物件去接收了這個函式物件f,在最後一行開啟fengshan的開關 -- fengshan(),這時候函式物件f中的程式碼,才剛被執行

如果看不懂的話,建議從風扇圖片開始再看一遍,如果你看懂了,建議你也再看一遍,至此,我們就已經完成了一個裝飾器,為了更方便使用裝飾器,Python給我們提供了更簡便的方法

def wrapper(func):
  def f():
    print('在wrapper中執行func前')
    func()
    print('在wrapper中執行func後')
  return f
@wrapper
def fengshan():
  print('出風')
fengshan()

返回

在wrapper中執行func前

出風

在wrapper中執行func後

補充知識

如果你已經理解了裝飾器的執行邏輯,你也就會知道如何讓裝飾器支援帶引數的方法,這也是我們寫裝飾器的常規操作

def wrapper(func):
  def f(*args,**kwargs):
    print('在wrapper中執行func前')
    func(*args,**kwargs)
    print('在wrapper中執行func後')
  return f
@wrapper
def fengshan(str_obj):
  print(str_obj)
fengshan(str_obj='出風')

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。