python裝飾器

1.閉包

閉包就是變量在函數(shù)里定義后,函數(shù)運(yùn)行后就不會(huì)存在了,但是這個(gè)變量在函數(shù)運(yùn)行中被其中的嵌套函數(shù)引用了,就留在了嵌套函數(shù)里面,這就是閉。閉包是理解裝飾器的基礎(chǔ)。

#閉包
def main():
    msg = 'i am writing log'
    #嵌套函數(shù)
    def mylog():
        print(msg)
    return mylog
colosure = main()
#這里沒(méi)有輸出
colosure()
#output:i am writing log

2.閉包轉(zhuǎn)為裝飾器

裝飾器則是將一般的變量變?yōu)榱撕瘮?shù),這個(gè)函數(shù)在后期會(huì)運(yùn)行

def mylog(func):
    print('logging')
    def wrapper():
        print('before')
        func()
        print('after')
    return main
def main1():
    print('i am main1')
mylog(main1)()
mylog(main1).__name__
'''
#print信息
logging
before
i am main1
after
logging
#output
'wrapper'
'''

3.簡(jiǎn)化

用@符號(hào)進(jìn)行簡(jiǎn)化調(diào)用,簡(jiǎn)化后可以用于函數(shù)執(zhí)行的性能測(cè)試

def mylog1(func):
    print('logging')
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper
@mylog1
def main1():
    print('i am another')
main1()
'''
#print信息
logging
before
i am another
after
'''

4.對(duì)裝飾器進(jìn)行改進(jìn)

我們可以看到我們雖然做到了在函數(shù)前后打印或做其他東西,但是函數(shù)的主體已經(jīng)變了,不是mylog了,變成原來(lái)最初返回的函數(shù)體了。但是到這里我們可以做一下簡(jiǎn)化了

def mylog2(func):
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper

@mylog2
def main2():
    print('i am in main2')
main2()
print(main2.__name__)
'''
#print信息
before
i am in main2
after
wrapper
'''

簡(jiǎn)化后運(yùn)行更簡(jiǎn)單了,不用兩個(gè)括號(hào)了,這就是@的用處。

5.保留初始函數(shù)信息

函數(shù)體(名字等屬性)依然是最后返回的wrapper,需要繼續(xù)改進(jìn)。引入functool中的wrapper函數(shù),對(duì)屬性等信息進(jìn)行復(fù)制保留。它能把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,這使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息了。

from functools import wraps

def mylog3(func):
    @wraps(func)
    def wrapper():
        print('before')
        func()
        print('after')
    return wrapper
@mylog3
def main3():
    print('i am in main3')
main3()
print(main3.__name__)
'''
#print信息
before
i am in main3
after
main3
'''

這里我們看到main3的函數(shù)不再被wrapper替換了

6.帶參數(shù)的裝飾器(3層)

有時(shí)候裝飾器也需要帶參數(shù),這個(gè)時(shí)候?yàn)榱艘褏?shù)傳進(jìn)去,需要在最外層套一層,用于傳入裝飾器的參數(shù),這樣就成了3層,再內(nèi)一層才開(kāi)始傳入函數(shù)

#帶參數(shù)的裝飾器
def mylog5(logname = '1'):
    def logdeco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('before')
            print(logname)
            return func(*args, **kwargs)
        return wrapper
    return logdeco

@mylog5()
def main5():
    print('i am in ',main5.__name__)
main5()
'''
before
1
i am in  main5
'''
@mylog5(logname='2')
def main6():
    print('i am in ',main6.__name__)
main6()
print(main6.__name__)    
'''
before
2
i am in  main6
'''

7.裝飾器類(配合call函數(shù)使用)

甚至可以用建立一個(gè)裝飾器類如下,讓call函數(shù)(定義在類上的call函數(shù))多接受一個(gè)函數(shù)作為參數(shù),就可以再@logit的時(shí)候,直接用作裝飾器了

from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 現(xiàn)在,發(fā)送一個(gè)通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做別的
        print('notify1')
        pass

@logit()
def myfunc1():
    pass 
myfunc1()
'''
print信息
myfunc1 was called
notify1
'''

8.對(duì)類的繼承和具化

作為類當(dāng)然也可以繼承,所以可以在這個(gè)基礎(chǔ)上構(gòu)造更具體的log函數(shù),比如

class email_logit(logit):
    '''
    一個(gè)logit的實(shí)現(xiàn)版本,可以在函數(shù)調(diào)用時(shí)發(fā)送email給管理員
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 發(fā)送一封email到self.email,省略
        print('notify')
        pass
@email_logit()
def myfunc2():
    pass
myfunc2()
'''
print信息
myfunc2 was called
notify
'''

9.常用裝飾器

@property,把類內(nèi)的方法當(dāng)作類屬性使用,必須要保證類方法有返回值
@classmethod,類方法,不需要self參數(shù),但第一個(gè)參數(shù)需要是表示自身類的cls參數(shù)
@staticmethod,靜態(tài)方法,不需要表示自身對(duì)象的self和自身類的cls參數(shù),就跟使用函數(shù)一樣。

#1.
class car():
    def __init__(self,name,price):
        self._name = name
        self._price = price
    @property
    def car_name(self):
        return self._name
    @car_name.setter
    def car_name(self,val):
        self._name = val
    def price(self):
        return self._price
benz = car('benz',30)
print(benz.car_name)
#output:benz
benz.car_name='bmw'
print(benz.car_name)
#output:bmw

#2.

#3.

10.裝飾器的順序

最上面的是最外層

@a
@b
@c
def main():
    pass
a(b(c(main())))

11.使用場(chǎng)景

打印日志,檢查授權(quán)(如果有授權(quán)就執(zhí)行,沒(méi)有就不執(zhí)行),性能測(cè)試,事務(wù)處理,緩存
這里貼一下緩存的代碼

#裝飾器實(shí)現(xiàn)緩存

def cache(func):
    cache = {}
    @wraps(func)
    def wrap(*args):
#        print(args)
        if args not in cache:
            cache[args] = func(*args)
            #print(cache)
        return cache[args]
    return wrap

class Solution:
    @cache
    def fib(self, N):
        if N < 2:
            return N
        else:
            return self.fib(N - 2) + self.fib(N - 1)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容