搜尋
首頁後端開發Python教學python裝飾器深入學習_python

python裝飾器深入學習_python

Apr 08, 2018 am 11:11 AM
python學習

這篇文章主要深入學習了python裝飾器的相關資料,什麼是裝飾器?裝飾器遵循的原則等,具有一定的參考價值,有興趣的小伙伴們可以參考一下

什麼是裝飾器

在我們的軟體產品升級時,常常需要為各個函數新增功能,而在我們的軟體產品中,相同的函數可能會被調用上百次,這種情況是很常見的,如果我們一個個的修改,那我們的碼農豈不要掛掉了(有人就說了,你笨呀,修改函數定義不就行了!同學,你醒醒吧,如果要新加的功能會修改參數,或者返回值呢?)。這時候,就是我們裝飾器大顯神通的時候了。裝飾器就可以實現,在不改變原函數的呼叫形式下(即函數的透明化處理),為函數新增功能的作用。如何實現,以及實現原理,下文會詳解。

裝飾者遵循的原則

裝飾器,顧名思義就是起裝飾的作用,既然是裝飾,那麼被裝飾的物件是啥樣就是啥樣,不能有絲毫改變。 在這裡,我們寫裝飾器就是必須掌握不能修改被修飾函數的原始碼這條鐵律。 如何遵循這條鐵律,我們還需還需做一些鋪墊,必須先了解三個概念,如下:

函數名稱即「變數」

在python中,函數名其實就像是c語言的函數指針,代表的是我們的函數位址,只有解釋器取得到這個位址,它才會去執行這塊記憶體的代碼。因此,本質上,函數名就和不同變數沒什麼區別,只不過函數名和普通變數所指涉的那塊記憶體的使用方式不同罷了,這些都是底層解釋器的機制所決定的,對於程式猿來說,都是透明的,所以,我們可以認為兩者是沒有差別的。

高階函數

什麼是高階函數其實很簡單,把握兩個原則就好:

  • #形式參數有函數名稱

  • 傳回值有函數名稱

#只要滿足這兩個原則之一,就可以稱為是高階函數。翻回頭來看,這裡出現了我們上面說的函數名,仔細體會一下,我們在這裡不就是把其當成實參看待的嗎?

巢狀函數字

#什麼是巢狀函數其實也很簡單,要把握一個原則就好:

  • 在一個函數的函數體中去定義另一個函數

在這裡需要強調的是,函數定義時是不會執行函數體的,就和定義變數是不會去讀取變數裡的內容一樣。這一點至關重要,對於我們理解裝飾器實作原理非常有幫助。

如何寫裝飾器

有了上文的鋪墊,在現在來詳解一下如何寫裝飾器,就好理解多了。

裝飾器本質

  其實裝飾器本質上就是一個函數,它也有函數名,參數和傳回值。但在python中,我們用「@auth」來表示。

@auth    # 其等价于:func = auth(func)
def func():
  print("func called")

 這個範例就是python中如何修飾func函數的格式,當然我們還沒有實作我們的裝飾函數。我們要注意的是註解裡寫的內容,我們可以看出:

  • 裝飾函數其實是高階函數(參數和回傳值都為函數名)。

  • 「auth(func)」是在呼叫我們的裝飾器函數,也就是裝飾器函數的函數體會被執行,一定要記好這一點。

設計想法

#裝飾子即是函數,又有上述介紹的等價關係,那我們就可以這樣設計我們的裝飾器:

  • 在我們裝飾器的函數體內去定義一個新的函數,在這個新定義的函數內去呼叫被修飾的函數,同時,在被修飾的函數的上下文去加入新功能。最後,利用裝飾器函數的回傳值來傳回我們新定義函數的函數名稱。

  • 由此可以知道,「func = auth(func)」中的回傳值func表示的就是在裝飾器中新定義的函數的函數名稱。

前面做了大量的鋪墊,就是想在這裡揭示裝飾器的實作機制,其實沒什麼什麼的,很簡單:

  • 装饰器机制改变了被修饰函数的函数名表示的地址数据。说白了就是,被修饰前,函数名代表的是A内存块;被修饰后,函数名代表的是B内存块;只不过,在执行B内存块时,会调用A内存块罢了。B内存块中的代码就是我们新加的功能。而这种机制的实现,使用了“高阶函数”和“嵌套函数”的机制。

  • 最终的效果就是,但在调用被修饰过的函数时,其实调用的不是原来的内存块,而是修饰器新申请的内存块。

第一步:设计装饰器函数

装饰器函数定义跟普通函数定义没什么区别,关键是函数体怎么写的问题。这里,为了便于理解,先用无参数的装饰器函数说明。

#装饰器函数定义格式
def deco(func):
  '''函数体...'''
return func

这里说的无参数,指的是没有除了“func”之外的参数
难点是函数体的编写,下面的示例先告诉你为什么要有第二步:

#使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
def deco(func):
  print("before myfunc() called.")
  func()
  print("after myfunc() called.")
  return func
 
@deco
def myfunc():
  print("myfunc() called.")
 
myfunc()
myfunc()
 
#output:
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.

由输出结果可以看出,我们的装饰器并没有生效。别跟我说装饰器只生效了一次,那是大家忽略了“@deco”的等效机制。解释到“@deco”时,会解释成“myfunc = deco(myfunc)”。注意了,前面我提到了,这里其实在调用deco函数的,因此,deco的函数体会被执行。所以output的前三行并不是调用myfunc函数时产生的效果,那有怎能说装饰器生效了一次呢?第二步就是解决装饰器没生效的问题的。

第二步:包装被修饰函数

#基本格式
def deco(func):
  def _deco()
    #新增功能
    #...
    #...
    func() #别修饰函数调用
  return_deco

 下面给出个示例:

#使用内嵌包装函数来确保每次新函数都被调用,
#内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
 
def deco(func):
  def _deco():
    print("before myfunc() called.")
    func()
    print("after myfunc() called.")
    # 不需要返回func,实际上应返回原函数的返回值
  return _deco
 
@deco
def myfunc():
  print("myfunc() called.")
  return 'ok'
 
myfunc()
 
#output:
before myfunc() called.
myfunc() called.
after myfunc() called.

  第三步:被修饰函数参数和返回值透明化处理

当完成了第二步时,其实装饰器已经完成了主要部分,下面就是对被修饰函数的参数和返回值的处理。这样才能真正实现装饰器的铁律。话不多说,直接上代码:

#基本格式
def deco(func):
  def _deco(*args, **kwargs) #参数透明化
    #新增功能
    #...
    #...
    res = func(*args, **kwargs) #别修饰函数调用
    return res #返回值透明化
  return_deco

通过上面的分析知:

参数透明化:当我们在调用被装饰后的函数时,其实调用的时这里的_deco函数。那么,我们就给_deco函数加上可变参数,并把得到的可变参数传递给func函数不就可以了。
返回值透明化:和参数透明化同理,给_deco函数定义返回值,并返回func的返回值就可以了。

透明化处理就是这么简单!至此,我们的装饰器编写完成。给个示例吧:

#对带参数的函数进行装饰,
#内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
 
def deco(func):
  def _deco(*agrs, **kwagrs):
    print("before myfunc() called.")
    ret = func(*agrs, **kwagrs)
    print(" after myfunc() called. result: %s" % ret)
    return ret
  return _deco
 
@deco
def myfunc(a, b):
  print(" myfunc(%s,%s) called." % (a, b))
  return a + b
 
print("sum=",myfunc(1, 2))
print("sum=",myfunc(3, 4))
 
#output:
before myfunc() called.
 myfunc(1,2) called.
 after myfunc() called. result: 3
sum= 3
before myfunc() called.
 myfunc(3,4) called.
 after myfunc() called. result: 7
sum= 7

装饰器进阶

带参数装饰器

装饰器即然也是函数,那么我们也可以给其传递参数。我这里说的是:“@auth(auth_type = 'type1')”这中形式哟。先上个代码吧:

#基本格式
def deco(deco_type)
  def _deco(func):
    def __deco(*args, **kwargs) #参数透明化
      #新增功能
      #...
      #...
      print("deco_type:",deco_type) #使用装饰器参数
      res = func(*args, **kwargs) #别修饰函数调用
      return res #返回值透明化
    return __deco
  return_deco

 说白了,就是在原来的装饰器的基础上再在最外层套一个deco函数,并用其来接收装饰器参数。由于是在最外层套了一个函数,那么这个函数的形参的作用范围就是函数体内部,所以里面的函数定义中随便用啦,就这么任性。
那怎么理解解释器的解析过程呢?在这里,只要我们明白一点就好,那就是: “@auth(auth_type = 'type1')”等价于“func = auth(auth_type = 'type1')(func)” 解释器会先翻译“auth(auth_type = 'type1')”,再将其返回值(假设给了_func这个不存在的函数名)当作函数指针,这里的_func函数名代表的是_deco,然后再去执行“func = _func(func)”,而这个func函数名代表的其实就是__deco。

至此,就达到了通过装饰器来传参的目的。给个示例吧:

#示例7: 在示例4的基础上,让装饰器带参数,
#和上一示例相比在外层多了一层包装。
#装饰函数名实际上应更有意义些
 
def deco(deco_type):
  def _deco(func):
    def __deco(*args, **kwagrs):
      print("before %s called [%s]." % (func.__name__, deco_type))
      func(*args, **kwagrs)
      print(" after %s called [%s]." % (func.__name__, deco_type))
    return __deco
  return _deco
 
@deco("mymodule")
def myfunc():
  print(" myfunc() called.")
 
@deco("module2")
def myfunc2():
  print(" myfunc2() called.")
 
myfunc()
myfunc2()
 
#output:
before myfunc called [mymodule].
 myfunc() called.
 after myfunc called [mymodule].
before myfunc2 called [module2].
 myfunc2() called.
 after myfunc2 called [module2].

多重装饰器修饰函数

如果说,我上面说的内容都理解了,那么这个东东,就太简单不过了。不就是把我们的是装饰器当中被修饰的函数,对它进行装饰吗?但我在这里还想说的是,我们换个角度看问题。我们的关注点放在原来的被修饰的函数上,就会发现,NB呀,我可以给它添加若干个功能撒。给个示例吧:

def deco(deco_type):
  def _deco(func):
    def __deco(*args, **kwagrs):
      print("before %s called [%s]." % (func.__name__, deco_type))
      func(*args, **kwagrs)
      print(" after %s called [%s]." % (func.__name__, deco_type))
    return __deco
  return _deco
 
@deco("module1")
@deco("mymodule")
def myfunc():
  print(" myfunc() called.")
 
@deco("module2")
def myfunc2():
  print(" myfunc2() called.")
 
myfunc()
 
#output:
before __deco called [module1].
before myfunc called [mymodule].
 myfunc() called.
 after myfunc called [mymodule].
 after __deco called [module1].

 注意结果哟,@deco("module1"),来修饰的deco("mymdule")的,和我们想的是一样的,完美!

相关推荐:

深度理解Python装饰器的概念和含义

python裝飾器的深入淺出

#

以上是python裝飾器深入學習_python的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python的主要目的:靈活性和易用性Python的主要目的:靈活性和易用性Apr 17, 2025 am 12:14 AM

Python的靈活性體現在多範式支持和動態類型系統,易用性則源於語法簡潔和豐富的標準庫。 1.靈活性:支持面向對象、函數式和過程式編程,動態類型系統提高開發效率。 2.易用性:語法接近自然語言,標準庫涵蓋廣泛功能,簡化開發過程。

Python:多功能編程的力量Python:多功能編程的力量Apr 17, 2025 am 12:09 AM

Python因其簡潔與強大而備受青睞,適用於從初學者到高級開發者的各種需求。其多功能性體現在:1)易學易用,語法簡單;2)豐富的庫和框架,如NumPy、Pandas等;3)跨平台支持,可在多種操作系統上運行;4)適合腳本和自動化任務,提升工作效率。

每天2小時學習Python:實用指南每天2小時學習Python:實用指南Apr 17, 2025 am 12:05 AM

可以,在每天花費兩個小時的時間內學會Python。 1.制定合理的學習計劃,2.選擇合適的學習資源,3.通過實踐鞏固所學知識,這些步驟能幫助你在短時間內掌握Python。

Python與C:開發人員的利弊Python與C:開發人員的利弊Apr 17, 2025 am 12:04 AM

Python適合快速開發和數據處理,而C 適合高性能和底層控制。 1)Python易用,語法簡潔,適用於數據科學和Web開發。 2)C 性能高,控制精確,常用於遊戲和系統編程。

Python:時間投入和學習步伐Python:時間投入和學習步伐Apr 17, 2025 am 12:03 AM

學習Python所需時間因人而異,主要受之前的編程經驗、學習動機、學習資源和方法及學習節奏的影響。設定現實的學習目標並通過實踐項目學習效果最佳。

Python:自動化,腳本和任務管理Python:自動化,腳本和任務管理Apr 16, 2025 am 12:14 AM

Python在自動化、腳本編寫和任務管理中表現出色。 1)自動化:通過標準庫如os、shutil實現文件備份。 2)腳本編寫:使用psutil庫監控系統資源。 3)任務管理:利用schedule庫調度任務。 Python的易用性和豐富庫支持使其在這些領域中成為首選工具。

Python和時間:充分利用您的學習時間Python和時間:充分利用您的學習時間Apr 14, 2025 am 12:02 AM

要在有限的時間內最大化學習Python的效率,可以使用Python的datetime、time和schedule模塊。 1.datetime模塊用於記錄和規劃學習時間。 2.time模塊幫助設置學習和休息時間。 3.schedule模塊自動化安排每週學習任務。

Python:遊戲,Guis等Python:遊戲,Guis等Apr 13, 2025 am 12:14 AM

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)