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

python裝飾器簡(jiǎn)介---這一篇也許就夠了(推薦)

 更新時(shí)間:2019年04月01日 11:46:56   作者:小洋人最happy  
這篇文章主要介紹了python裝飾器簡(jiǎn)介,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Python裝飾器(decorator)是在程序開發(fā)中經(jīng)常使用到的功能,合理使用裝飾器,能讓我們的程序如虎添翼。

裝飾器引入

初期及問題誕生

假如現(xiàn)在在一個(gè)公司,有A B C三個(gè)業(yè)務(wù)部門,還有S一個(gè)基礎(chǔ)服務(wù)部門,目前呢,S部門提供了兩個(gè)函數(shù),供其他部門調(diào)用,函數(shù)如下:

def f1():
  print('f1 called')


def f2():
  print('f2 called')

在初期,其他部門這樣調(diào)用是沒有問題的,隨著公司業(yè)務(wù)的發(fā)展,現(xiàn)在S部門需要對(duì)函數(shù)調(diào)用假如權(quán)限驗(yàn)證,如果有權(quán)限的話,才能進(jìn)行調(diào)用,否則調(diào)用失敗。考慮一下,如果是我們,該怎么做呢?

方案集合

  1. 讓調(diào)用方也就是ABC部門在調(diào)用的時(shí)候,先主動(dòng)進(jìn)行權(quán)限驗(yàn)證
  2. S部門在對(duì)外提供的函數(shù)中,首先進(jìn)行權(quán)限認(rèn)證,然后再進(jìn)行真正的函數(shù)操作

問題

  1. 方案一,將本不該暴露給外層的權(quán)限認(rèn)證,暴露在使用方面前,同時(shí)如果有多個(gè)部門呢,要每個(gè)部門每個(gè)人都要周知到,你還不缺定別人一定會(huì)這么做,不靠譜。。。
  2. 方案二,看似看行,可是當(dāng)S部門對(duì)外提供更多的需要進(jìn)行權(quán)限驗(yàn)證方法時(shí),每個(gè)函數(shù)都要調(diào)用權(quán)限驗(yàn)證,同樣也實(shí)在費(fèi)勁,不利于代碼的維護(hù)性和擴(kuò)展性

那么,有沒有一種方法能夠遵循代碼的開放閉合原則,來完美的解決此問題呢?

裝飾器引入

答案肯定是有的,不然真的是弱爆了。先看代碼

def w1(func):
  def inner():
    print('...驗(yàn)證權(quán)限...')
    func()

  return inner


@w1
def f1():
  print('f1 called')


@w1
def f2():
  print('f2 called')


f1()
f2()

輸出結(jié)果為

...驗(yàn)證權(quán)限...
f1 called
...驗(yàn)證權(quán)限...
f2 called

可以通過代碼及輸出看到,在調(diào)用f1 f2 函數(shù)時(shí),成功進(jìn)行了權(quán)限驗(yàn)證,那么是怎么做到的呢?其實(shí)這里就使用到了裝飾器,通過定義一個(gè)閉包函數(shù)w1,在我們調(diào)用函數(shù)上通過關(guān)鍵詞@w1,這樣就對(duì)f1 f2函數(shù)完成了裝飾。

裝飾器原理

首先,開看我們的裝飾器函數(shù)w1,該函數(shù)接收一個(gè)參數(shù)func,其實(shí)就是接收一個(gè)方法名,w1內(nèi)部又定義一個(gè)函數(shù)inner,在inner函數(shù)中增加權(quán)限校驗(yàn),并在驗(yàn)證完權(quán)限后調(diào)用傳進(jìn)來的參數(shù)func,同時(shí)w1的返回值為內(nèi)部函數(shù)inner,其實(shí)就是一個(gè)閉包函數(shù)。

然后,再來看一下,在f1上增加@w1,那這是什么意思呢?當(dāng)python解釋器執(zhí)行到這句話的時(shí)候,會(huì)去調(diào)用w1函數(shù),同時(shí)將被裝飾的函數(shù)名作為參數(shù)傳入(此時(shí)為f1),根據(jù)閉包一文分析,在執(zhí)行w1函數(shù)的時(shí)候,此時(shí)直接把inner函數(shù)返回了,同時(shí)把它賦值給f1,此時(shí)的f1已經(jīng)不是未加裝飾時(shí)的f1了,而是指向了w1.inner函數(shù)地址。

接下來,在調(diào)用f1()的時(shí)候,其實(shí)調(diào)用的是w1.inner函數(shù),那么此時(shí)就會(huì)先執(zhí)行權(quán)限驗(yàn)證,然后再調(diào)用原來的f1(),該處的f1就是通過裝飾傳進(jìn)來的參數(shù)f1。

這樣下來,就完成了對(duì)f1的裝飾,實(shí)現(xiàn)了權(quán)限驗(yàn)證。

裝飾器知識(shí)點(diǎn)

執(zhí)行時(shí)機(jī)

了解了裝飾器的原理后,那么它的執(zhí)行時(shí)機(jī)是什么樣呢,接下來就來看一下。
國(guó)際慣例,先上代碼

def w1(fun):
  print('...裝飾器開始裝飾...')

  def inner():
    print('...驗(yàn)證權(quán)限...')
    fun()

  return inner


@w1
def test():
  print('test')

test()

輸出結(jié)果為

...裝飾器開始裝飾...
...驗(yàn)證權(quán)限...
test

由此可以發(fā)現(xiàn),當(dāng)python解釋器執(zhí)行到@w1時(shí),就開始進(jìn)行裝飾了,相當(dāng)于執(zhí)行了如下代碼:

test = w1(test)

兩個(gè)裝飾器執(zhí)行流程和裝飾結(jié)果

當(dāng)有兩個(gè)或兩個(gè)以上裝飾器裝飾一個(gè)函數(shù)時(shí),那么執(zhí)行流程和裝飾結(jié)果是什么樣的呢?同樣,還是以代碼來說明問題。

def makeBold(fun):
  print('----a----')

  def inner():
    print('----1----')
    return '<b>' + fun() + '</b>'

  return inner


def makeItalic(fun):
  print('----b----')

  def inner():
    print('----2----')
    return '<i>' + fun() + '</i>'

  return inner


@makeBold
@makeItalic
def test():
  print('----c----')
  print('----3----')
  return 'hello python decorator'


ret = test()
print(ret)

輸出結(jié)果:

----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>

可以發(fā)現(xiàn),先用第二個(gè)裝飾器(makeItalic)進(jìn)行裝飾,接著再用第一個(gè)裝飾器(makeBold)進(jìn)行裝飾,而在調(diào)用過程中,先執(zhí)行第一個(gè)裝飾器(makeBold),接著再執(zhí)行第二個(gè)裝飾器(makeItalic)。

為什么呢,分兩步來分析一下。

  1. 裝飾時(shí)機(jī) 通過上面裝飾時(shí)機(jī)的介紹,我們可以知道,在執(zhí)行到@makeBold的時(shí)候,需要對(duì)下面的函數(shù)進(jìn)行裝飾,此時(shí)解釋器繼續(xù)往下走,發(fā)現(xiàn)并不是一個(gè)函數(shù)名,而又是一個(gè)裝飾器,這時(shí)候,@makeBold裝飾器暫停執(zhí)行,而接著執(zhí)行接下來的裝飾器@makeItalic,接著把test函數(shù)名傳入裝飾器函數(shù),從而打印'b',在makeItalic裝飾完后,此時(shí)的test指向makeItalic的inner函數(shù)地址,這時(shí)候有返回來執(zhí)行@makeBold,接著把新test傳入makeBold裝飾器函數(shù)中,因此打印了'a'。
  2. 在調(diào)用test函數(shù)的時(shí)候,根據(jù)上述分析,此時(shí)test指向makeBold.inner函數(shù),因此會(huì)先打印‘1‘,接下來,在調(diào)用fun()的時(shí)候,其實(shí)是調(diào)用的makeItalic.inner()函數(shù),所以打印‘2‘,在makeItalic.inner中,調(diào)用的fun其實(shí)才是我們最原聲的test函數(shù),所以打印原test函數(shù)中的‘c‘,‘3‘,所以在一層層調(diào)完之后,打印的結(jié)果為<b><i>hello python decorator</i></b> 。

對(duì)無參函數(shù)進(jìn)行裝飾

上面例子中的f1 f2都是對(duì)無參函數(shù)的裝飾,不再單獨(dú)舉例

對(duì)有參函數(shù)進(jìn)行裝飾

在使用中,有的函數(shù)可能會(huì)帶有參數(shù),那么這種如何處理呢?

代碼優(yōu)先:

def w_say(fun):
  """
  如果原函數(shù)有參數(shù),那閉包函數(shù)必須保持參數(shù)個(gè)數(shù)一致,并且將參數(shù)傳遞給原方法
  """

  def inner(name):
    """
    如果被裝飾的函數(shù)有行參,那么閉包函數(shù)必須有參數(shù)
    :param name:
    :return:
    """
    print('say inner called')
    fun(name)

  return inner


@w_say
def hello(name):
  print('hello ' + name)


hello('wangcai')

輸出結(jié)果為:

say inner called
hello wangcai

具體說明代碼注釋已經(jīng)有了,就不再單獨(dú)說明了。
此時(shí),也許你就會(huì)問了,那是一個(gè)參數(shù)的,如果多個(gè)或者不定長(zhǎng)參數(shù)呢,該如何處理呢?看看下面的代碼你就秒懂了。

def w_add(func):
  def inner(*args, **kwargs):
    print('add inner called')
    func(*args, **kwargs)

  return inner


@w_add
def add(a, b):
  print('%d + %d = %d' % (a, b, a + b))


@w_add
def add2(a, b, c):
  print('%d + %d + %d = %d' % (a, b, c, a + b + c))


add(2, 4)
add2(2, 4, 6)

輸出結(jié)果為:

add inner called
2 + 4 = 6
add inner called
2 + 4 + 6 = 12

利用python的可變參數(shù)輕松實(shí)現(xiàn)裝飾帶參數(shù)的函數(shù)。

對(duì)帶返回值的函數(shù)進(jìn)行裝飾

下面對(duì)有返回值的函數(shù)進(jìn)行裝飾,按照之前的寫法,代碼是這樣的

def w_test(func):
  def inner():
    print('w_test inner called start')
    func()
    print('w_test inner called end')
  return inner


@w_test
def test():
  print('this is test fun')
  return 'hello'


ret = test()
print('ret value is %s' % ret)

輸出結(jié)果為:

w_test inner called start
this is test fun
w_test inner called end
ret value is None

可以發(fā)現(xiàn),此時(shí),并沒有輸出test函數(shù)的‘hello',而是None,那是為什么呢,可以發(fā)現(xiàn),在inner函數(shù)中對(duì)test進(jìn)行了調(diào)用,但是沒有接受不了返回值,也沒有進(jìn)行返回,那么默認(rèn)就是None了,知道了原因,那么來修改一下代碼:

def w_test(func):
  def inner():
    print('w_test inner called start')
    str = func()
    print('w_test inner called end')
    return str

  return inner


@w_test
def test():
  print('this is test fun')
  return 'hello'


ret = test()
print('ret value is %s' % ret)

輸出結(jié)果:

w_test inner called start
this is test fun
w_test inner called end
ret value is hello

這樣就達(dá)到預(yù)期,完成對(duì)帶返回值參數(shù)的函數(shù)進(jìn)行裝飾。

帶參數(shù)的裝飾器

介紹了對(duì)帶參數(shù)的函數(shù)和有返回值的函數(shù)進(jìn)行裝飾,那么有沒有帶參數(shù)的裝飾器呢,如果有的話,又有什么用呢?
答案肯定是有的,接下來通過代碼來看一下吧。

def func_args(pre='xiaoqiang'):
  def w_test_log(func):
    def inner():
      print('...記錄日志...visitor is %s' % pre)
      func()

    return inner

  return w_test_log


# 帶有參數(shù)的裝飾器能夠起到在運(yùn)行時(shí),有不同的功能

# 先執(zhí)行func_args('wangcai'),返回w_test_log函數(shù)的引用
# @w_test_log
# 使用@w_test_log對(duì)test_log進(jìn)行裝飾
@func_args('wangcai')
def test_log():
  print('this is test log')


test_log()

輸出結(jié)果為:

...記錄日志...visitor is wangcai
this is test log

簡(jiǎn)單理解,帶參數(shù)的裝飾器就是在原閉包的基礎(chǔ)上又加了一層閉包,通過外層函數(shù)func_args的返回值w_test_log就看出來了,具體執(zhí)行流程在注釋里已經(jīng)說明了。
好處就是可以在運(yùn)行時(shí),針對(duì)不同的參數(shù)做不同的應(yīng)用功能處理。

通用裝飾器

介紹了這么多,在實(shí)際應(yīng)用中,如果針對(duì)沒個(gè)類別的函數(shù)都要寫一個(gè)裝飾器的話,估計(jì)就累死了,那么有沒有通用萬能裝飾器呢,答案肯定是有的,廢話不多說,直接上代碼。

def w_test(func):
  def inner(*args, **kwargs):
    ret = func(*args, **kwargs)
    return ret

  return inner


@w_test
def test():
  print('test called')


@w_test
def test1():
  print('test1 called')
  return 'python'


@w_test
def test2(a):
  print('test2 called and value is %d ' % a)


test()
test1()
test2(9)

輸出結(jié)果為:

test called
test1 called
test2 called and value is 9

把上面幾種示例結(jié)合起來,就完成了通用裝飾器的功能,原理都同上,就不過多廢話了。

類裝飾器

裝飾器函數(shù)其實(shí)是一個(gè)接口約束,它必須接受一個(gè)callable對(duì)象作為參數(shù),然后返回一個(gè)callable對(duì)象。
在python中,一般callable對(duì)象都是函數(shù),但是也有例外。比如只要某個(gè)對(duì)象重寫了call方法,那么這個(gè)對(duì)象就是callable的。
當(dāng)創(chuàng)建一個(gè)對(duì)象后,直接去執(zhí)行這個(gè)對(duì)象,那么是會(huì)拋出異常的,因?yàn)樗皇莄allable,無法直接執(zhí)行,但進(jìn)行修改后,就可以直接執(zhí)行調(diào)用了,如下

class Test(object):
  def __call__(self, *args, **kwargs):
    print('call called')


t = Test()
print(t())

輸出為:

call called

下面,引入正題,看一下如何用類裝飾函數(shù)。

class Test(object):
  def __init__(self, func):
    print('test init')
    print('func name is %s ' % func.__name__)
    self.__func = func

  def __call__(self, *args, **kwargs):
    print('裝飾器中的功能')
    self.__func()


@Test
def test():
  print('this is test func')


test()

輸出結(jié)果為:

test init
func name is test
裝飾器中的功能
this is test func

和之前的原理一樣,當(dāng)python解釋器執(zhí)行到到@Test時(shí),會(huì)把當(dāng)前test函數(shù)作為參數(shù)傳入Test對(duì)象,調(diào)用init方法,同時(shí)將test函數(shù)指向創(chuàng)建的Test對(duì)象,那么在接下來執(zhí)行test()的時(shí)候,其實(shí)就是直接對(duì)創(chuàng)建的對(duì)象進(jìn)行調(diào)用,執(zhí)行其call方法。

好了,到目前為止,基本把python裝飾器及相關(guān)知識(shí)點(diǎn)講完了,如有問題,歡迎指出哈~

以上所述是小編給大家介紹的python裝飾器簡(jiǎn)介詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • python正則爬取某段子網(wǎng)站前20頁段子(request庫)過程解析

    python正則爬取某段子網(wǎng)站前20頁段子(request庫)過程解析

    這篇文章主要介紹了python正則爬取某段子網(wǎng)站前20頁段子(request庫)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • python讀寫xml文件實(shí)例詳解嘛

    python讀寫xml文件實(shí)例詳解嘛

    這篇文章主要為大家詳細(xì)介紹了python讀寫xml文件的實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • python鏈表類中獲取元素實(shí)例方法

    python鏈表類中獲取元素實(shí)例方法

    在本篇文章里小編給大家整理的是一篇關(guān)于python鏈表類中獲取元素實(shí)例方法,有興趣的朋友們可以學(xué)習(xí)下。
    2021-02-02
  • 在jupyter notebook中使用pytorch的方法

    在jupyter notebook中使用pytorch的方法

    這篇文章主要介紹了在jupyter notebook中使用pytorch的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • pycharm激活方法到2099年(激活流程)

    pycharm激活方法到2099年(激活流程)

    這篇文章主要介紹了pycharm激活方法到2099年,文末給大家提到了idea和pycharm最新版激活方法,非常不錯(cuò)對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 全面理解python命名空間字典

    全面理解python命名空間字典

    本文主要介紹了全面理解python命名空間字典,python的命名空間由字典實(shí)現(xiàn),屬性為鍵,對(duì)象為值,通過屬性找到對(duì)象,下面就來具體了解一下,感興趣的可以了解一下
    2023-12-12
  • Python對(duì)象屬性自動(dòng)更新操作示例

    Python對(duì)象屬性自動(dòng)更新操作示例

    這篇文章主要介紹了Python對(duì)象屬性自動(dòng)更新操作,結(jié)合實(shí)例形式對(duì)比分析了Python對(duì)象屬性自動(dòng)更新的原理,并改進(jìn)了屬性互聯(lián)操作實(shí)現(xiàn)方法,需要的朋友可以參考下
    2018-06-06
  • Python字符串str超詳細(xì)詳解(適合新手!)

    Python字符串str超詳細(xì)詳解(適合新手!)

    str函數(shù)是Python的內(nèi)置函數(shù),它將參數(shù)轉(zhuǎn)換成字符串類型,即人適合閱讀的形式,下面這篇文章主要給大家介紹了關(guān)于Python字符串str超詳細(xì)詳解的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • 淺談python中的錯(cuò)誤與異常

    淺談python中的錯(cuò)誤與異常

    寫Python代碼的小伙伴不可避免地會(huì)遇到代碼執(zhí)行錯(cuò)誤和異常,這次就來詳細(xì)的總結(jié)一下python中的錯(cuò)誤和異常
    2021-06-06
  • python+pyqt實(shí)現(xiàn)12306圖片驗(yàn)證效果

    python+pyqt實(shí)現(xiàn)12306圖片驗(yàn)證效果

    這篇文章主要為大家詳細(xì)介紹了python+pyqt實(shí)現(xiàn)12306圖片驗(yàn)證效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評(píng)論