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)