欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

快速了解Python中的裝飾器

 更新時間:2018年01月11日 10:06:16   作者:GanZiQim  
這篇文章主要介紹了快速了解Python中的裝飾器,具有一定借鑒價值,需要的朋友可以參考下

需要理解的一些概念

要理解Python中的裝飾器,我覺得還是應(yīng)該從最基本的概念開始:

裝飾器模式:所謂的裝飾器模式,可以簡單地理解為“在不改變原有內(nèi)部實現(xiàn)的情況下,為函數(shù)或者類添加某種特性”。這樣我們就可以將一些與業(yè)務(wù)無關(guān)、具有通用性的代碼抽象出來,作為裝飾器附加到需要這些代碼的函數(shù)或者類之上。用面向切面編程的思想解釋就是“裝飾器應(yīng)該是一個切面”。

函數(shù)是一等公民:意思就是函數(shù)可以被當(dāng)成普通變量一樣使用。在Python中,可以把函數(shù)賦值給變量,可以將函數(shù)作為其它函數(shù)的參數(shù),也可以將函數(shù)作為其它函數(shù)的返回值。

閉包:我們都知道局部作用域可以引用全局作用域中的變量,相似的,當(dāng)一個函數(shù)內(nèi)部又定義了其它函數(shù)的時候,內(nèi)部函數(shù)可以使用外部函數(shù)所在作用域的變量,這就是閉包。

從最簡單的裝飾器做起

理解完以上的概念之后,我們嘗試一下利用這些特性實現(xiàn)一個簡單的裝飾器。

首先明確一下需求,我們有時候會需要在函數(shù)調(diào)用時打印一個相應(yīng)的日志,雖然可以通過在所有需要打印日志的函數(shù)代碼中嵌入打印日志的代碼來實現(xiàn),但這種方法不僅增加了許多重復(fù)代碼,而且在業(yè)務(wù)代碼中嵌入與業(yè)務(wù)無關(guān)的代碼增加了整體的耦合度。因此,我們需要實現(xiàn)一個裝飾器,這個裝飾器在函數(shù)調(diào)用時可以打印一個日志記錄函數(shù)調(diào)用行為。

如果我們有以下函數(shù)foo,代表具體的業(yè)務(wù)函數(shù):

def foo():
  print('in function foo')

我們設(shè)想通過調(diào)用foo = deco(foo)實現(xiàn)給函數(shù)foo增加打印日志的功能,并且不影響它原有的業(yè)務(wù)。那么在這種設(shè)想下,裝飾器deco應(yīng)該也是一個函數(shù),它接收另一個函數(shù)作為輸入,并返回一個新的、經(jīng)過裝飾的函數(shù)。在Python中,我們可以這么寫:

def deco(func): # 接收一個函數(shù)作為參數(shù)
  def new_func():
    print(f'[log] run function {func.__name__}') # 此處使用了Python3.6的格式化字符串
    func() # 閉包,在內(nèi)部函數(shù)中使用了外部函數(shù)的變量
  return new_func # 將新函數(shù)作為返回值返回

執(zhí)行一下試試效果:

>>> foo = deco(foo)
>>> foo()
[log] run function foo
in function foo

不錯,至此我們已經(jīng)實現(xiàn)了一個最簡單的裝飾器!在上面的代碼中,裝飾器deco接收任意的函數(shù)作為參數(shù),再在內(nèi)部構(gòu)造另一個函數(shù),利用閉包的特性,可以在新函數(shù)里調(diào)用存在于裝飾器deco局部作用域中的函數(shù)func。

神奇的@

按照上面那么寫,每次我們都得為需要裝飾的函數(shù)賦一個新值,萬一函數(shù)或者裝飾器的數(shù)量增加了,手動寫賦值和函數(shù)調(diào)用就會變得非常麻煩。那么在Python中,有沒有更優(yōu)雅的寫法呢?答案是有的,你只需要一個@符號。

在Python中,當(dāng)我們需要一個裝飾器時:

def deco(func):
  def new_func():
    print(f'[log] run function {func.__name__}')
    func()
  return new_func

@deco
def foo():
  print('in function foo')

這個地方我們省略了函數(shù)的賦值,直接在函數(shù)foo定義的上一行加上@deco進行裝飾。運行一下試試看:

>>> foo()
[log] run function foo
in function foo

是不是感覺很神奇?其實這里面沒什么魔法,只不過是Python在處理函數(shù)定義的代碼時,幫你在其中把foo=deco(foo)的邏輯加上了而已。

裝飾器也想要參數(shù)

上面的代碼實現(xiàn)了在業(yè)務(wù)函數(shù)調(diào)用之前打印日志的功能,那如果我們需要在業(yè)務(wù)代碼執(zhí)行完之后打印一條自定義的消息,怎么辦呢?我們必須要讓我們的裝飾器可以接收自定義參數(shù)。

上面提到過,Python做的只是當(dāng)寫了@deco時,把調(diào)用deco(func)的結(jié)果賦值給它裝飾的函數(shù)而已。順著這個邏輯,當(dāng)我們需要一個帶參數(shù)的裝飾器時,代碼上應(yīng)該是寫為@deco('some message'),這時Python將調(diào)用deco(msg)(func)的結(jié)果賦值給foo。那么事情就變得簡單了,我們只需要在上面代碼的基礎(chǔ)上嵌套一層函數(shù):

def deco(msg):
  def inner_deco(func):
    def new_func():
      print(f'[log] run function {func.__name__}')
      func()
      print(f'[log] {msg}')
    return new_func
  return inner_deco

@deco('some message')
def foo():
  print('in function foo')

執(zhí)行一下試試:

>>> foo()
[log] run function foo
in function foo
[log] some message

不支持帶參數(shù)的被裝飾函數(shù)的裝飾器不是好裝飾器

上面的代碼還是有問題,因為我們只考慮了函數(shù)foo沒有參數(shù)時的情況,萬一函數(shù)foo帶了參數(shù),這個裝飾器就會丟失參數(shù)信息,這不是一個合格的裝飾器應(yīng)該出現(xiàn)的情況。所以,我們借助Python中的*args和**kwargs使被裝飾的函數(shù)可以支持傳入任意參數(shù):

def deco(msg):
  def inner_deco(func):
    def new_func(*args, **kwargs):
      print(f'[log] run function {func.__name__}')
      func(*args, **kwargs)
      print(f'[log] {msg}')
    return new_func
  return inner_deco

@deco('some message')
def foo(a, b=None):
  print('in function foo')
  print(f'a is {a} & b is ')

這樣一來,無論函數(shù)foo的參數(shù)列表是怎么樣的都不會有問題了:

>>> foo('hello')
[log] run function foo
in function foo
a is hello & b is None
[log] some message

不支持有返回值的被裝飾函數(shù)的裝飾器不是好裝飾器

別忘了,到目前為止,我們寫的函數(shù)foo都沒有返回值,如果函數(shù)foo有返回值怎么辦呢?我想你心里應(yīng)該有答案了:

def deco(msg):
  def inner_deco(func):
    def new_func(*args, **kwargs):
      print(f'[log] run function {func.__name__}')
      rlt = func(*args, **kwargs)
      print(f'[log] {msg}')
      return rlt
    return new_func
  return inner_deco

@deco('some message')
def foo(a, b=None):
  print('in function foo')
  print(f'a is {a} & b is ')
  return 'ok'

由于裝飾器在原函數(shù)執(zhí)行完之后還有別的操作,所以應(yīng)該把返回值暫存起來,等到裝飾器的邏輯執(zhí)行完畢,才返回最終結(jié)果。這就是我們的最終版裝飾器了!

>>> rlt = foo('a')
[log] run function foo
in function foo
a is a & b is None
[log] some message
>>> print(rlt)
ok

有沒有更騷的操作?

當(dāng)然有??!我標(biāo)題都這么寫了難不成會沒有?

在Python中,你可以使用類來作為裝飾器:

class Deco(object):
  def __call__(self, func):
    def new_func():
      print(f'[log] run function {func.__name__}')
      func()
    return new_func

@Deco()
def foo():
  print('in function foo')
>>> foo()
[log] run function foo
in function foo

這么做的好處就是可以利用類更好地管理參數(shù)和調(diào)用邏輯,比起之前三層函數(shù)嵌套的形式是不是清晰多了?

在Python中,你還可以使用裝飾器來裝飾一個類,比如這樣:

def add_doc(doc):
  def deco(cls):
    cls.__doc__ = doc
    return cls
  return deco

@add_doc('this is the doc of Cls')
class Cls(object):
  pass

來看看效果:

>>> help(Cls)
Help on class Cls in module __main__:

class Cls(builtins.object)
 | this is the doc of Cls

上面的代碼只是一個示例,展示裝飾器怎么裝飾一個類而已,不是說在實際情況下應(yīng)該這么用。大部分的情況下,我們對于類的拓展應(yīng)該是通過繼承而不是裝飾。

具體怎么巧妙地利用裝飾器就要靠大家發(fā)揮自己的想象力了。

總結(jié)

以上就是本文關(guān)于快速了解Python中的裝飾器的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關(guān)文章

  • 用C++封裝MySQL的API的教程

    用C++封裝MySQL的API的教程

    這篇文章主要介紹了用C++封裝MySQL的API的教程,包括對語句拼裝器SQLJoin的介紹,需要的朋友可以參考下
    2015-05-05
  • Python用input輸入列表的實例代碼

    Python用input輸入列表的實例代碼

    在本篇文章里小編給大家整理的是關(guān)于Python用input輸入列表的實例代碼,需要的朋友們可以參考下。
    2020-02-02
  • Python入門篇之編程習(xí)慣與特點

    Python入門篇之編程習(xí)慣與特點

    本文是Python入門篇的第一篇文章,主要講述了Python編程習(xí)慣和特點等一些基礎(chǔ)知識,有需要的朋友可以參考下
    2014-10-10
  • Python 實現(xiàn)黑客帝國中的字符雨的示例代碼

    Python 實現(xiàn)黑客帝國中的字符雨的示例代碼

    這篇文章主要介紹了Python 實現(xiàn)黑客帝國中的字符雨的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • python 高階函數(shù)簡單介紹

    python 高階函數(shù)簡單介紹

    這篇文章主要介紹了python 高階函數(shù)的相關(guān)資料,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2021-02-02
  • Python中的Broadcast機制

    Python中的Broadcast機制

    這篇文章主要介紹了Python中的Broadcast機制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Python猴子補丁Monkey Patch用法實例解析

    Python猴子補丁Monkey Patch用法實例解析

    這篇文章主要介紹了Python猴子補丁Monkey Patch用法實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • 詳解Python中的內(nèi)置常量的使用

    詳解Python中的內(nèi)置常量的使用

    Python作為一種功能強大的編程語言,提供了豐富的內(nèi)置常量來簡化編程過程,本文將深入探討Python中的內(nèi)置常量,并提供豐富的示例代碼來演示其用法,希望對大家有所幫助
    2024-03-03
  • Python?獲取今天任意時刻的時間戳的方法

    Python?獲取今天任意時刻的時間戳的方法

    本文主要介紹了Python?獲取今天任意時刻的時間戳的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2022-06-06
  • python實現(xiàn)輸出一個序列的所有子序列示例

    python實現(xiàn)輸出一個序列的所有子序列示例

    今天小編就為大家分享一篇python實現(xiàn)輸出一個序列的所有子序列示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11

最新評論