裝飾器
1.
用于拓展原來函數(shù)功能的一種函數(shù)
2.
返回函數(shù)的函數(shù)
3.
在不用更改原函數(shù)的代碼前提下給函數(shù)增加新的功能
//如果沒有裝飾器
def hello():
print('hello world')
def test():
print('test...')
def hello_wrapper():
//新的函數(shù),包裹原來的hello
print('進(jìn)入函數(shù)hello')
hello()
print('結(jié)束執(zhí)行hello')
def test_wrapper():
print('進(jìn)入函數(shù)hello')
test()
print('結(jié)束執(zhí)行hello')
if __name__ == '__main__':
//要實(shí)現(xiàn)這樣的功能,就要建立多個(gè)wrapper(),而且有變化需要修改多個(gè)地方,不好維護(hù)
hello_wrapper()
test_wrapper()
//實(shí)現(xiàn)裝飾器,用裝飾器實(shí)現(xiàn)
def log(func)
//記錄函數(shù)執(zhí)行的日志
def wrapper():
print('start...')
func()
print('end...')
def log_in(func)
//記錄函數(shù)執(zhí)行的日志
def wrapper():
print('開始...')
func()
print('結(jié)束...')
@log
def hello():
//簡單功能模擬
print('hello world')
@log
@log_in //裝飾器可以使用多個(gè),按順序嵌套執(zhí)行
def test()
print('test...')
if __name__ == '__main__':
hello()
test()
//帶參數(shù)的裝飾器
def log(name = None): //允許默認(rèn)值,可以不傳參
def decorator(fun):
def wrapper():
print('{0} start...'.format(name))
fun()
print('{0} end...'.format(name))
return wrapper
return decorator //返回函數(shù)不要帶括號
@log('hello') //也可以不傳參 @log()
def hello():
print('hello world')
@log('test')
def test():
print('test...')
if __name__ == '__main__':
hello()
test()
//如果帶參數(shù)的裝飾器,同時(shí)自定義的函數(shù)本身也帶參數(shù)和返回值,如何傳遞使用
def log(name = None): //允許默認(rèn)值,可以不傳參
def decorator(func):
def wrapper(*args, **kwargs): //這里就要傳入add(a, b)的參數(shù).用兩個(gè)魔法參數(shù),一個(gè)元組,一個(gè)字典
print('{0} start...'.format(name))
rest = func(*args, **kwargs) //這里就要傳入add(a, b)的參數(shù),用一個(gè)臨時(shí)變量保存返回值
print('{0} end...'.format(name))
return rest //為了執(zhí)行上一句打印,在這里返回臨時(shí)變量值
return wrapper
return decorator //返回函數(shù)不要帶括號
@log('hello') //也可以不傳參 @log()
def hello():
print('hello world')
@log('test')
def test():
print('test...')
def add(a, b, *args, **kwargs): //有參數(shù),有返回值,如何使用,帶兩個(gè)魔法參數(shù),傳更多的變量
return a + b
if __name__ == '__main__':
//hello()
//test()
rest = add(5, 6, k = 5, v = 6)
///裝飾器之wraps
def log(name = None): //允許默認(rèn)值,可以不傳參
def decorator(func):
@wraps(func) //j加上這個(gè)裝飾器,就可以解決注釋__doc__和名字__name__等被改變的問題
def wrapper(*args, **kwargs): //這里就要傳入add(a, b)的參數(shù).用兩個(gè)魔法參數(shù),一個(gè)元組,一個(gè)字典
//注釋:裝飾器內(nèi)部的函數(shù)
print('{0} start...'.format(name))
rest = func(*args, **kwargs) //這里就要傳入add(a, b)的參數(shù),用一個(gè)臨時(shí)變量保存返回值
print('{0} end...'.format(name))
return rest //為了執(zhí)行上一句打印,在這里返回臨時(shí)變量值
//原始hello()的注釋名字是在這附近發(fā)生了改變 ,如果不用@wraps需要在下面進(jìn)行手動更改
//wrapper.__doc__ = func.__doc__
//wrapper.__name__ = func.__name__
//但是每個(gè)都這么操縱很麻煩,所以pyton提供了一個(gè)@wraps用來使用,放在裝飾器內(nèi)部函數(shù)上
return wrapper
return decorator //返回函數(shù)不要帶括號
@log('hello') //也可以不傳參 @log()
def hello():
//注釋:簡單功能模擬
print('hello world')
if __name__ == '__main__':
print('doc: {0}'.format(hello.__doc__))
print('doc: {0}'.format(hello.__name__)) //結(jié)果注釋和名字都不是hello()的,而是變成了裝飾器內(nèi)部的函數(shù)wrapper()的注釋和名字,這時(shí)候就需要用到@wraps
hello()
///裝飾器注意事項(xiàng):
1,用@wraps 還原原始函數(shù)的注釋\名字等信息
2,用魔法傳參 *args, **kwargs 解決自己的函數(shù)傳參問題
3,自己的函數(shù)有返回值的,把函數(shù)返回值用臨時(shí)變量保存起來,執(zhí)行完裝飾器內(nèi)邏輯后,再return
4,也可以用于類
///類的裝飾器
def f(self): //可以在這里擴(kuò)展類的方法
print('{0}: 我愛吃東西'.format(self.name))
print('000000') //
def eat(cls):
//cls.eat = lambda self: print('{0}: 我愛吃東西'.format(self.name)) //簡單的用lambda,更豐富可以用下面方法
cls.eat = f
return cls
@eat
class Cat(object):
//貓類
def init(self, name)
self.name = name
if name == 'main':
cat = Cat('小黑')
cat.eat()