裝飾器

裝飾是為函數(shù)和類指定管理代碼的一種方式.裝飾器本身的形式是處理其他的可調(diào)用對象的可調(diào)用的對象。

  • 函數(shù)裝飾器在函數(shù)定義的時候進行名稱重綁定,提供一個邏輯層來管理函數(shù)和方法或隨后對他們的調(diào)用
  • 類裝飾器在類定義的時候進行名稱重綁定,提供一個邏輯層來管理類,或管理隨后調(diào)用他們所創(chuàng)建的示例
基礎(chǔ)知識
@decorator
def F(arg):pass
F(99)   #調(diào)用函數(shù)

def F(arg):pass
F = decorator(F)
F(99)    #調(diào)用函數(shù)
這一自動名稱重綁定在def語句上有效,不管它針對一個簡單的函數(shù)或是類中的一個方法。當隨后調(diào)用F函數(shù)的時候,它自動調(diào)用裝飾器所返回的對象,該對象可能是實現(xiàn)了所需的包裝邏輯的另一個對象,或者是最初的函數(shù)本身。

裝飾器在裝飾的時候調(diào)用,并且當隨后調(diào)用最初的函數(shù)名的時候,它所返回的調(diào)用對象將被調(diào)用。裝飾器本身接收被裝飾的函數(shù),返回的調(diào)用對象會接收隨后傳遞給給裝飾函數(shù)的名稱的任何參數(shù)。

def decorator(F):
    def wrapper(*args):
        print(args)
    return wrapper
@decorator
def func(x,y):
    pass
if __name__ == '__main__':
    func(6,7)    #調(diào)用方法實際上調(diào)用的是wrapper(6,7)
    #func = decorator(func)  #func現(xiàn)在是decorator返回的包裝器函數(shù)wrapper
    #func(6,7)
class decorator:
    def \_\_init__(self,func):
        self.func = func
    def \_\_call__(self,*args):
        print(args)
        self.func(*args)
@decorator
def func(x,y):
    print(x+y)
if __name__ == '__main__':
    func(6,7)
###
現(xiàn)在,隨后在調(diào)用func的時候,它確實會調(diào)用裝飾器所創(chuàng)建的實例\__call__運算符重載方法;然后\__call__方法可能運行最初的func,因為它在一個實例屬性中仍然可用。當按照這種方式編寫代碼的時候,每個裝飾的函數(shù)都會產(chǎn)生一個新的實例來保持狀態(tài)

--------------------------------------------------------------------------
def decorator(F):
    def wrapper(*args):
        F(*args)
    return wrapper
class C:
    @decorator
    def method(self,x,y):
        print(x*y)
if __name__ == '__main__':
    c = C()
    c.method(6,7)
       42

類裝飾器

基礎(chǔ)知識

@decorator
class C:
    ....
x = C(99)
相當于
class C:......
C = decorator(C)
x = C(99)
也是返回一個可調(diào)用對象的一個可調(diào)用對象,因此大多數(shù)函數(shù)和類的組合已經(jīng)足夠了

一個例子

#-*-coding:UTF-8-*-

def decorator(cls):
    class Wrapper:
        def __init__(self, *arg):
            self.wrappered = cls(*arg)
        def __getattr__(self,name):
            return getattr(self.wrappered,name)
    return Wrapper
@decorator
class C:
    def __init__(self,x,y):
        self.attr = 'spam'
if __name__ == '__main__':
    x = C(6,7)
    print(x.attr)
"""
這個例子中,裝飾器把C類的名稱重新綁定到另一個類(Wrapper),這個類(Wrapper)在一個封閉的作用域中保持了最初的類(C)
,并且當調(diào)用它的時候,創(chuàng)建并嵌入了最初的類的一個實例。
當隨后從該實例獲取一個屬性的時候,包裝器的__getattr__攔截了它,并且將其委托給最初的類的嵌入的實例
"""

裝飾器嵌套
語法

@A
@B
@C
def f(...):
  ...
相當于:
def f(...):....
f = A(B(C(f)))

例如:

def d1(F):return lambda:'X'+F()
def d2(F):return lambda:'Y'+F()
def d3(F):return lambda:'Z'+F()
@d1
@d2
@d3
def func():
    return 'spam'
if __name__ == '__main__':
    print(func())
>>>XYZspam

裝飾器參數(shù)
函數(shù)裝飾器和類裝飾器似乎都能接受參數(shù),盡管實際上這些參數(shù)傳遞給了真正返回裝飾器的一個可回調(diào)對象,而裝飾器反過來又返回一個可調(diào)用對象
語法

@decorator(A,B)
def F(arg):...
F(99)
自動映射到其對等的形式,其中裝飾器是一個可調(diào)用對象,它返回實際的裝飾器。返回的裝飾器反過來返回可調(diào)用的對象
def F(arg):
  ...
F = decorator(A,B)(F)
F(99)

跟蹤調(diào)用的例子

"""
這個類裝飾的每個函數(shù)將如何創(chuàng)建一個新的實例,帶有自己保存的函數(shù)對象和
調(diào)用計數(shù)器。還要注意觀察,*args參數(shù)語法如何來打包和解包任意的多個
傳入?yún)?shù)。這個類裝飾器可以用來包裝帶有任意多個參數(shù)的任何函數(shù)
"""
class tracer:
    def __init__(self,func):
        self.calls = 0
        self.func = func  #把被包裝的函數(shù)存儲為自己的實例屬性
    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)
if __name__ == '__main__':
    spam(1,2,3)
>>>
call 1 to spam
6
###
類裝飾器tracer不適合裝飾其他類中的方法,例如
class Person:
    """docstring for 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ù)列表中傳遞Person主體

使用嵌套函數(shù)來裝飾方法
如果想讓函數(shù)小黃時期在簡單函數(shù)和類方法上都能工作,最直接的解決方法在于把自己的函數(shù)裝飾器編寫成嵌套的def,以便對于包裝器類和主類實例都不依賴單個的self實例參數(shù)

#-*-coding:UTF-8-*-
def tracer(func):
    calls = 0
    def onCall(*args,**kwargs):
        nonlocal calls
        calls+=1
        print('call %s to %s'%(calls,func.__name__))
        return func(*args,**kwargs)
    return onCall
@tracer
def spam(a,b,c):
    print(a+b+c)

class Person:
    def __init__(self,name,pay):
        self.name = name
        self.pay = pay
    # @tracer
    def giveRaise(self,percent):
        self.pay *= (1.0+percent)
    @tracer
    def lastName(self):
        return self.name.split()[-1]
if __name__ == '__main__':
    spam(1,2,3)
    bob = Person('Bob Smith',50000)
    bob.giveRaise = tracer(bob.giveRaise) #裝飾器的原型
    bob.giveRaise(.10)
    print(int(bob.pay))

**添加裝飾器參數(shù)

#-*-coding:UTF-8-*-
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:
                format = '%s %s:%.5f,%.5f'
                values = (label,self.func.__name__,elapsed,self.alltime)
                print(format%values)
            return result
    return Timer

@timer(label='[CCC]==>')
def listcomp(N):
    return [x * 2 for x in range(N)]
if __name__ == '__main__':
    print(listcomp(50))

編寫類裝飾器

def Tracer(aClass):
    class Wrapper:
        def __init__(self,*args,**kargs):
            self.fetches = 0
            self.wrapped = aClass(*args,**kargs) #實例化aClass類的一個實例
        def __getattr__(self,attrname):
            print('Trace: '+attrname)
            self.fetches += 1
            return getattr(self.wrapped,attrname)  #調(diào)用self.wrapped的attrname方法(屬性)
    return Wrapper

"""
類裝飾器 編寫為一個類的情況
class Tracer:
    def __init__(self,aClass):          #運行到@的時候會觸發(fā)\__init__
        self.aClass = aClass
    def __call__(self,*args):           #下面實例化主類的時候,Spam()會調(diào)用\__call__。需要返回一個主類的對象
        self.wrapped = self.aClass(*args)
        return self
    def __getattr__(self,attrname):
        print('Trace: '+attrname)
        return getattr(self.wrapped,attrname)
        
"""
@Tracer
class Spam:
    def display(self,a):      #添加一個參數(shù)
        print('Spam '*8+a)
@Tracer
class Person:
    def __init__(self,name,hours,rate):
        self.name = name
        self.hours = hours
        self.rate = rate
    def pay(self):
        return self.hours * self.rate

if __name__ == '__main__':
    food = Spam()
    food.display('a')       #必須提供一個參數(shù)
    print(food.fetches)
    bob = Person('Bob',40,50)
    print(bob.name)

實現(xiàn)私有屬性

#-*-coding:UTF-8-*-

traceMe = False
def trace(*args):
    if traceMe:print('['+' '.join(map(str,args))+']')

def Private(*privates):
    print(privates)
    def onDecorator(aClass):
        class onInstance:
            def __init__(self,*args,**kargs):
                self.wrapped = aClass(*args,**kargs) #自己初始化是也會調(diào)用__setattr__
                print(aClass.__name__)
            def __getattr__(self,attr):
                trace('get:',attr)
                if attr in privates:
                    raise TypeError('private attr')
                else:
                    return getattr(self.wrapped,attr)
            def __setattr__(self,attr,value):
                trace('set: ',attr,value)
                print('--->',attr)
                if attr == 'wrapped':
                    self.__dict__[attr] = value
                elif attr in privates:
                    raise TypeError('private attr change')
                else:
                    setattr(self.wrapped,attr,value)
        return onInstance
    return onDecorator

if __name__ == '__main__':
    traceMe = True
    @Private('data','size')
    class Doubler:
        def __init__(self,label,start):
            self.label = label
            self.data = start
        def size(self):
            return len(self.data)
        def double(self):
            for i in range(self.size()):
                self.data[i] = self.data[i] * 2
        def display(self):
            print('%s=>%s'%(self.label,self.data))
    """
    Doubler = Private('data','size')(Doubler)
    Private('data','size') 返回的是onDecorator函數(shù),而此函數(shù)接收一個類
    并在該函數(shù)返回一個包裝類onInstance,onInstance在封閉的onDecorator函數(shù)中
    aClass是傳遞給onDecorator函數(shù)的參數(shù),可以在onInstance內(nèi)部使用aClass
    """
    X = Doubler('X is',[1,2,3])
    print(X.label)
    X.display()
    # X.data = [1,2,3,4]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內(nèi)容

  • 每個人都有的內(nèi)褲主要功能是用來遮羞,但是到了冬天它沒法為我們防風御寒,咋辦?我們想到的一個辦法就是把內(nèi)褲改造一下,...
    chen_000閱讀 1,367評論 0 3
  • **裝飾器是一個很著名的設計模式,經(jīng)常被用于有切面需求的場景,較為經(jīng)典的有插入日志、性能測試、事務處理等。裝飾器是...
    牛崽兒酷閱讀 325評論 0 0
  • 元類與裝飾器的關(guān)系 在class語句的末尾,類裝飾器把類名重新綁定到一個函數(shù)的結(jié)果。 元類通過一條class語句的...
    低吟淺唱1990閱讀 408評論 0 0
  • Python裝飾器的高級用法(翻譯) 原文地址https://www.codementor.io/python/t...
    城南道閱讀 4,821評論 1 22
  • 當陽光穿越萬水千山,俯首窗前,親昵枯萎的玫瑰花時,早上的困乏隨著這抹浪漫煙消云散,在喝完杯中最后一滴牛奶后,我決定...
    文藝到老閱讀 492評論 0 2