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

Python函數(shù)裝飾器實(shí)現(xiàn)方法詳解

 更新時間:2018年12月22日 08:51:14   作者:KLeonard  
這篇文章主要介紹了Python函數(shù)裝飾器實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Python函數(shù)裝飾器的概念、功能、用法及相關(guān)操作注意事項,需要的朋友可以參考下

本文實(shí)例講述了Python函數(shù)裝飾器實(shí)現(xiàn)方法。分享給大家供大家參考,具體如下:

編寫函數(shù)裝飾器

這里主要介紹編寫函數(shù)裝飾器的相關(guān)內(nèi)容。

跟蹤調(diào)用

如下代碼定義并應(yīng)用一個函數(shù)裝飾器,來統(tǒng)計對裝飾的函數(shù)的調(diào)用次數(shù),并且針對每一次調(diào)用打印跟蹤信息。

class tracer:
  def __init__(self,func):
    self.calls = 0
    self.func = func
  def __call__(self,*args):
    self.calls += 1
    print('call %s to %s' %(self.calls, self.func.__name__))
    self.func(*args)
@tracer
def spam(a, b, c):
  print(a + b + c)

這是一個通過類裝飾的語法寫成的裝飾器,測試如下:

>>> spam(1,2,3)
call 1 to spam
6
>>> spam('a','b','c')
call 2 to spam
abc
>>> spam.calls
2
>>> spam
<__main__.tracer object at 0x03098410>

運(yùn)行的時候,tracer類和裝飾的函數(shù)分開保存,并且攔截對裝飾的函數(shù)的隨后的調(diào)用,以便添加一個邏輯層來統(tǒng)計和打印每次調(diào)用。

裝飾之后,spam實(shí)際上是tracer類的一個實(shí)例。

@裝飾器語法避免了直接地意外調(diào)用最初的函數(shù)??紤]如下所示的非裝飾器的對等代碼:

calls = 0
def tracer(func,*args):
  global calls
  calls += 1
  print('call %s to %s'%(calls,func.__name__))
  func(*args)
def spam(a,b,c):
  print(a+b+c)

測試如下:

>>> spam(1,2,3)
6
>>> tracer(spam,1,2,3)
call 1 to spam
6

這一替代方法可以用在任何函數(shù)上,且不需要特殊的@語法,但是和裝飾器版本不同,它在代碼中調(diào)用函數(shù)的每個地方都需要額外的語法。盡管裝飾器不是必需的,但是它們通常是最為方便的。

擴(kuò)展——支持關(guān)鍵字參數(shù)

下述代碼時前面例子的擴(kuò)展版本,添加了對關(guān)鍵字參數(shù)的支持:

class tracer:
  def __init__(self,func):
    self.calls = 0
    self.func = func
  def __call__(self,*args,**kargs):
    self.calls += 1
    print('call %s to %s' %(self.calls, self.func.__name__))
    self.func(*args,**kargs)
@tracer
def spam(a, b, c):
  print(a + b + c)
@tracer
def egg(x,y):
  print(x**y)

測試如下:

>>> spam(1,2,3)
call 1 to spam
6
>>> spam(a=4,b=5,c=6)
call 2 to spam
15
>>> egg(2,16)
call 1 to egg
65536
>>> egg(4,y=4)
call 2 to egg
256

也可以看到,這里的代碼同樣使用【類實(shí)例屬性】來保存狀態(tài),即調(diào)用的次數(shù)self.calls。包裝的函數(shù)和調(diào)用計數(shù)器都是針對每個實(shí)例的信息。

使用def函數(shù)語法寫裝飾器

使用def定義裝飾器函數(shù)也可以實(shí)現(xiàn)相同的效果。但是有一個問題,我們也需要封閉作用域中的一個計數(shù)器,它隨著每次調(diào)用而更改。我們可以很自然地想到全局變量,如下:

calls = 0
def tracer(func):
  def wrapper(*args,**kargs):
    global calls
    calls += 1
    print('call %s to %s'%(calls,func.__name__))
    return func(*args,**kargs)
  return wrapper
@tracer
def spam(a,b,c):
  print(a+b+c)
@tracer
def egg(x,y):
  print(x**y)

這里calls定義為全局變量,它是跨程序的,是屬于整個模塊的,而不是針對每個函數(shù)的,這樣的話,對于任何跟蹤的函數(shù)調(diào)用,計數(shù)器都會遞增,如下測試:

>>> spam(1,2,3)
call 1 to spam
6
>>> spam(a=4,b=5,c=6)
call 2 to spam
15
>>> egg(2,16)
call 3 to egg
65536
>>> egg(4,y=4)
call 4 to egg
256

可以看到針對spam函數(shù)和egg函數(shù),程序用的是同一個計數(shù)器。

那么如何實(shí)現(xiàn)針對每一個函數(shù)的計數(shù)器呢,我們可以使用Python3中新增的nonlocal語句,如下:

def tracer(func):
  calls = 0
  def wrapper(*args,**kargs):
    nonlocal calls
    calls += 1
    print('call %s to %s'%(calls,func.__name__))
    return func(*args,**kargs)
  return wrapper
@tracer
def spam(a,b,c):
  print(a+b+c)
@tracer
def egg(x,y):
  print(x**y)
spam(1,2,3)
spam(a=4,b=5,c=6)
egg(2,16)
egg(4,y=4)

運(yùn)行如下:

call 1 to spam
6
call 2 to spam
15
call 1 to egg
65536
call 2 to egg
256

這樣,將calls變量定義在tracer函數(shù)內(nèi)部,使之存在于一個封閉的函數(shù)作用域中,之后通過nonlocal語句來修改這個作用域,修改這個calls變量。如此便可以實(shí)現(xiàn)我們所需求的功能。

陷阱:裝飾類方法

【注意,使用類編寫的裝飾器不能用于裝飾某一類中帶self參數(shù)的的函數(shù),這一點(diǎn)在Python裝飾器基礎(chǔ)中介紹過】

即如果裝飾器是如下使用類編寫的:

class tracer:
  def __init__(self,func):
    self.calls = 0
    self.func = func
  def __call__(self,*args,**kargs):
    self.calls += 1
    print('call %s to %s'%(self.calls,self.func.__name__))
    return self.func(*args,**kargs)

當(dāng)它裝飾如下在類中的方法時:

class Person:
  def __init__(self,name,pay):
    self.name = name
    self.pay = pay
  @tracer
  def giveRaise(self,percent):
    self.pay *= (1.0 + percent)

這時程序肯定會出錯。問題的根源在于,tracer類的__call__方法的self——它是一個tracer實(shí)例,當(dāng)我們用__call__把裝飾方法名重綁定到一個類實(shí)例對象的時候,Python只向self傳遞了tracer實(shí)例,它根本沒有在參數(shù)列表中傳遞Person主體。此外,由于tracer不知道我們要用方法調(diào)用處理的Person實(shí)例的任何信息,沒有辦法創(chuàng)建一個帶有一個實(shí)例的綁定的方法,所以也就沒有辦法正確地分配調(diào)用。

這時我們只能通過嵌套函數(shù)的方法來編寫裝飾器。

計時調(diào)用

下面這個裝飾器將對一個裝飾的函數(shù)的調(diào)用進(jìn)行計時——既有針對一次調(diào)用的時間,也有所有調(diào)用的總的時間。

import time
class timer:
  def __init__(self,func):
    self.func = func
    self.alltime = 0
  def __call__(self,*args,**kargs):
    start = time.clock()
    result = self.func(*args,**kargs)
    elapsed = time.clock()- start
    self.alltime += elapsed
    print('%s:%.5f,%.5f'%(self.func.__name__,elapsed,self.alltime))
    return result
@timer
def listcomp(N):
  return [x*2 for x in range(N)]
@timer
def mapcall(N):
  return list(map((lambda x :x*2),range(N)))
result = listcomp(5)
listcomp(50000)
listcomp(500000)
listcomp(1000000)
print(result)
print('allTime = %s'%listcomp.alltime)
print('')
result = mapcall(5)
mapcall(50000)
mapcall(500000)
mapcall(1000000)
print(result)
print('allTime = %s'%mapcall.alltime)
print('map/comp = %s '% round(mapcall.alltime/listcomp.alltime,3))

運(yùn)行結(jié)果如下:

listcomp:0.00001,0.00001
listcomp:0.00885,0.00886
listcomp:0.05935,0.06821
listcomp:0.11445,0.18266
[0, 2, 4, 6, 8]
allTime = 0.18266365607537918
mapcall:0.00002,0.00002
mapcall:0.00689,0.00690
mapcall:0.08348,0.09038
mapcall:0.16906,0.25944
[0, 2, 4, 6, 8]
allTime = 0.2594409060462425
map/comp = 1.42

這里要注意的是,map操作在Python3中返回一個迭代器,所以它的map操作不能和一個列表解析的工作直接對應(yīng),即實(shí)際上它并不花時間。所以要使用list(map())來迫使它像列表解析那樣構(gòu)建一個列表

添加裝飾器參數(shù)

有時我們需要裝飾器來做一個額外的工作,比如提供一個輸出標(biāo)簽并且可以打開或關(guān)閉跟蹤消息。這就需要用到裝飾器參數(shù)了,我們可以使用裝飾器參數(shù)來制定配置選項,這些選項可以根據(jù)每個裝飾的函數(shù)而編碼。例如,像下面這樣添加標(biāo)簽:

def timer(label = ''):
  def decorator(func):
    def onCall(*args):
      ...
      print(label,...)
    return onCall
  return decorator
@timer('==>')
def listcomp(N):...

我們可以將這樣的結(jié)果用于計時器中,來允許在裝飾的時候傳入一個標(biāo)簽和一個跟蹤控制標(biāo)志。比如,下面這段代碼:

import time
def timer(label= '', trace=True):
  class Timer:
    def __init__(self,func):
      self.func = func
      self.alltime = 0
    def __call__(self,*args,**kargs):
      start = time.clock()
      result = self.func(*args,**kargs)
      elapsed = time.clock() - start
      self.alltime += elapsed
      if trace:
        ft = '%s %s:%.5f,%.5f'
        values = (label,self.func.__name__,elapsed,self.alltime)
        print(format % value)
      return result
  return Timer

這個計時函數(shù)裝飾器可以用于任何函數(shù),在模塊中和交互模式下都可以。我們可以在交互模式下測試,如下:

>>> @timer(trace = False)
def listcomp(N):
  return [x * 2 for x in range(N)]
>>> x = listcomp(5000)
>>> x = listcomp(5000)
>>> x = listcomp(5000)
>>> listcomp
<__main__.timer.<locals>.Timer object at 0x036DCC10>
>>> listcomp.alltime
0.0011475424533080223
>>>
>>> @timer(trace=True,label='\t=>')
def listcomp(N):
  return [x * 2 for x in range(N)]
>>> x = listcomp(5000)
  => listcomp:0.00036,0.00036
>>> x = listcomp(5000)
  => listcomp:0.00034,0.00070
>>> x = listcomp(5000)
  => listcomp:0.00034,0.00104
>>> listcomp.alltime
0.0010432902706075842

更多關(guān)于Python相關(guān)內(nèi)容可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python Socket編程技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門與進(jìn)階經(jīng)典教程

希望本文所述對大家Python程序設(shè)計有所幫助。

相關(guān)文章

  • opencv中cv2.minAreaRect函數(shù)輸出角度問題詳解

    opencv中cv2.minAreaRect函數(shù)輸出角度問題詳解

    minAreaRect返回的數(shù)據(jù)包括了矩形的中心點(diǎn),寬、高,和旋轉(zhuǎn)角度,下面這篇文章主要給大家介紹了關(guān)于opencv中cv2.minAreaRect函數(shù)輸出角度問題的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • python 裝飾器功能以及函數(shù)參數(shù)使用介紹

    python 裝飾器功能以及函數(shù)參數(shù)使用介紹

    之前學(xué)習(xí)編程語言大多也就是學(xué)的很淺很淺,基本上也是很少涉及到裝飾器這些的類似的內(nèi)容??偸怯X得是一樣很神奇的東西,舍不得學(xué)(嘿嘿)。今天看了一下書籍。發(fā)現(xiàn)道理還是很簡單的
    2012-01-01
  • 瀏覽器常用基本操作之python3+selenium4自動化測試(基礎(chǔ)篇3)

    瀏覽器常用基本操作之python3+selenium4自動化測試(基礎(chǔ)篇3)

    瀏覽器常用基本操作有很多種,今天給大家介紹python3+selenium4自動化測試的操作方法,是最最基礎(chǔ)的一篇,對python3 selenium4自動化測試相關(guān)知識感興趣的朋友一起看看吧
    2021-05-05
  • pandas快速處理Excel,替換Nan,轉(zhuǎn)字典的操作

    pandas快速處理Excel,替換Nan,轉(zhuǎn)字典的操作

    這篇文章主要介紹了pandas快速處理Excel,替換Nan,轉(zhuǎn)字典的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • python logging添加filter教程

    python logging添加filter教程

    今天小編就為大家分享一篇python logging添加filter教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • Python如何使用type()函數(shù)查看數(shù)據(jù)的類型

    Python如何使用type()函數(shù)查看數(shù)據(jù)的類型

    這篇文章主要介紹了Python如何使用type()函數(shù)查看數(shù)據(jù)的類型,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Python webdriver.Chrome()的使用解讀

    Python webdriver.Chrome()的使用解讀

    這篇文章主要介紹了Python webdriver.Chrome()的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • pd.drop_duplicates刪除重復(fù)行的方法實(shí)現(xiàn)

    pd.drop_duplicates刪除重復(fù)行的方法實(shí)現(xiàn)

    drop_duplicates 方法實(shí)現(xiàn)對數(shù)據(jù)框 DataFrame 去除特定列的重復(fù)行,本文主要介紹了pd.drop_duplicates刪除重復(fù)行的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • py-charm延長試用期限實(shí)例

    py-charm延長試用期限實(shí)例

    今天小編就為大家分享一篇py-charm延長試用期限實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • python 實(shí)現(xiàn)圖片上傳接口開發(fā) 并生成可以訪問的圖片url

    python 實(shí)現(xiàn)圖片上傳接口開發(fā) 并生成可以訪問的圖片url

    今天小編就為大家分享一篇python 實(shí)現(xiàn)圖片上傳接口開發(fā) 并生成可以訪問的圖片url,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12

最新評論

洛宁县| 孝义市| 大埔区| 拉萨市| 成武县| 磐石市| 屏山县| 醴陵市| 通河县| 奉节县| 阿拉善盟| 阿荣旗| 石台县| 富川| 饶河县| 越西县| 额济纳旗| 德保县| 万宁市| 莎车县| 株洲县| 达拉特旗| 井研县| 武鸣县| 大连市| 钦州市| 巴青县| 铁岭县| 简阳市| 乌苏市| 西盟| 嘉鱼县| 吉木萨尔县| 武城县| 炎陵县| 隆昌县| 阿拉善右旗| 莱阳市| 宝清县| 永安市| 自贡市|