最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

裝飾器引入

初期及問題誕生

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

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


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

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

方案集合

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

問題

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

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

裝飾器引入

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

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

  return inner


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


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


f1()
f2()

輸出結(jié)果為

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

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

裝飾器原理

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

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

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

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

裝飾器知識點

執(zhí)行時機

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

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

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

  return inner


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

test()

輸出結(jié)果為

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

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

test = w1(test)

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

當有兩個或兩個以上裝飾器裝飾一個函數(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),先用第二個裝飾器(makeItalic)進行裝飾,接著再用第一個裝飾器(makeBold)進行裝飾,而在調(diào)用過程中,先執(zhí)行第一個裝飾器(makeBold),接著再執(zhí)行第二個裝飾器(makeItalic)。

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

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

對無參函數(shù)進行裝飾

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

對有參函數(shù)進行裝飾

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

代碼優(yōu)先:

def w_say(fun):
  """
  如果原函數(shù)有參數(shù),那閉包函數(shù)必須保持參數(shù)個數(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)有了,就不再單獨說明了。
此時,也許你就會問了,那是一個參數(shù)的,如果多個或者不定長參數(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ù)輕松實現(xiàn)裝飾帶參數(shù)的函數(shù)。

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

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

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),此時,并沒有輸出test函數(shù)的‘hello',而是None,那是為什么呢,可以發(fā)現(xiàn),在inner函數(shù)中對test進行了調(diào)用,但是沒有接受不了返回值,也沒有進行返回,那么默認就是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

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

帶參數(shù)的裝飾器

介紹了對帶參數(shù)的函數(shù)和有返回值的函數(shù)進行裝飾,那么有沒有帶參數(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ù)的裝飾器能夠起到在運行時,有不同的功能

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


test_log()

輸出結(jié)果為:

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

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

通用裝飾器

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

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ù)其實是一個接口約束,它必須接受一個callable對象作為參數(shù),然后返回一個callable對象。
在python中,一般callable對象都是函數(shù),但是也有例外。比如只要某個對象重寫了call方法,那么這個對象就是callable的。
當創(chuàng)建一個對象后,直接去執(zhí)行這個對象,那么是會拋出異常的,因為他不是callable,無法直接執(zhí)行,但進行修改后,就可以直接執(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

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

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

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

相關(guān)文章

  • 如何將yolov5中的PANet層改為BiFPN詳析

    如何將yolov5中的PANet層改為BiFPN詳析

    現(xiàn)在yolov5的neck用的是PANet,在efficient論文中提出了BiFPN結(jié)構(gòu),還有更加不錯的性能,下面這篇文章主要給大家介紹了關(guān)于如何將yolov5中的PANet層改為BiFPN的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 詳解如何用Python實現(xiàn)感知器算法

    詳解如何用Python實現(xiàn)感知器算法

    今天給大家?guī)淼氖顷P(guān)于Python的相關(guān)知識,文章圍繞著如何用Python實現(xiàn)感知器算法展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Python中的取模運算方法

    Python中的取模運算方法

    今天小編就為大家分享一篇Python中的取模運算方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-11-11
  • PyQt5類型判定+對象刪除操作

    PyQt5類型判定+對象刪除操作

    這篇文章主要介紹了PyQt5類型判定+對象刪除操作,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • Django數(shù)據(jù)庫表反向生成實例解析

    Django數(shù)據(jù)庫表反向生成實例解析

    這篇文章主要介紹了Django數(shù)據(jù)庫表反向生成實例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 使用numpy nonzero 找出非0元素

    使用numpy nonzero 找出非0元素

    這篇文章主要介紹了使用numpy nonzero 找出非0元素的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • 解決安裝python3.7.4報錯Can''''t connect to HTTPS URL because the SSL module is not available

    解決安裝python3.7.4報錯Can''''t connect to HTTPS URL because the S

    這篇文章主要介紹了解決安裝python3.7.4報錯Can't connect to HTTPS URL because the SSL module is not available,本文給大家簡單分析了錯誤原因,給出了解決方法,需要的朋友可以參考下
    2019-07-07
  • 詳解Python3中的 input() 函數(shù)

    詳解Python3中的 input() 函數(shù)

    這篇文章主要介紹了詳解Python3中的 input() 函數(shù)的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2020-03-03
  • Python個人博客程序開發(fā)實例用戶驗證功能

    Python個人博客程序開發(fā)實例用戶驗證功能

    這篇文章主要介紹了怎樣用Python來實現(xiàn)一個完整的個人博客系統(tǒng),我們通過實操上手的方式可以高效的鞏固所學的基礎(chǔ)知識,感興趣的朋友一起來看看吧
    2022-12-12
  • Python虛擬環(huán)境venv用法詳解

    Python虛擬環(huán)境venv用法詳解

    這篇文章主要介紹了Python虛擬環(huán)境venv用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05

最新評論

凭祥市| 石阡县| 屯留县| 五寨县| 江北区| 蓝田县| 行唐县| 视频| 淮阳县| 镇康县| 绥中县| 读书| 吉木萨尔县| 湖南省| 内黄县| 柳江县| 原平市| 浦东新区| 潍坊市| 鄂尔多斯市| 简阳市| 南召县| 青阳县| 平南县| 丹阳市| 教育| 合肥市| 台东市| 砀山县| 凤翔县| 浪卡子县| 固原市| 吉林省| 贵溪市| 达孜县| 农安县| 凉城县| 县级市| 周至县| 宣城市| 噶尔县|