1.認識裝飾器
在python中,對于一個函數,若想在其運行前后做點什么,那么裝飾器是再好不過的選擇,話不多說,上代碼。
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 01.py
__author__ = 'howie'
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("%s was called" % func.__name__)
func(*args, **kwargs)
return wrapper
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
outputs:
hello was called
Hello howie!
這段代碼,初看之下,確實不是很理解,接下來一步一步分析,看看裝飾器到底是怎么工作的。
2.裝飾器原理
在python中,方法允許作為參數傳遞,想在某個函數執(zhí)行前后加點料,也可以這樣簡單實現。
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-1.py
__author__ = 'howie'
def decorator(func):
print("%s was called" % func.__name__)
func()
def hello(name="howie"):
print("Hello %s!" % name)
decorator(hello)
由此,上面代碼也可以這樣寫:
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-2.py
__author__ = 'howie'
def decorator(func):
print("%s was called" % func.__name__)
func()
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello
兩段代碼執(zhí)行后:
outputs:
hello was called
Hello howie!
表面上看來,02-2.py代碼看起來也可以很好地執(zhí)行啊,可請注意,在末尾處,hello只是函數名稱,它并不能被調用,若執(zhí)行hello(),就會報TypeError: 'NoneType' object is not callable對象不能調用錯誤,這是自然,在decorator中func()直接將傳入的函數實例化了,有人會想,那如果這樣改呢?
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-3.py
__author__ = 'howie'
def decorator(func):
print("%s was called" % func.__name__)
return func
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
確實,這樣改是可以,可有沒有想過,若想在函數執(zhí)行結束后加點裝飾呢?這樣便行不通了,可能又有人會想,若這樣改呢?
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-4.py
__author__ = 'howie'
def decorator(func):
print("%s was called" % func.__name__)
func()
return bye
def bye():
print("bye~")
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
這樣寫看起來,恩,怎么說呢,總有種沒有意義的感覺,不如直接將在外部的函數放進decorator中,如下:
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-5.py
__author__ = 'howie'
def decorator(func):
def wrapper():
print("%s was called" % func.__name__)
func()
print("bye~")
return wrapper
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
執(zhí)行:
outputs:
hello was called
Hello howie!
bye~
怎么樣,輸出的結果是不是符合要求,其實簡單來看的話,可以這樣理解hello()==decorator(hello)()==wrapper(),最后其實就是執(zhí)行wrapper()函數而已,事實就是如此的簡單,不妨來驗證一下:
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-6.py
__author__ = 'howie'
def decorator(func):
def wrapper():
print("%s was called" % func.__name__)
func()
print("bye~")
return wrapper
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
print(hello.__name__)
outputs:
hello was called
Hello howie!
bye~
wrapper
果然就是執(zhí)行了wrapper函數,解決問題的同時也會出現新的問題,那便是代碼中本來定義的hello函數豈不是被wrapper函數覆蓋了,又該如何解決這個問題呢?這時候functions.wraps就可以登場了,代碼如下:
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 02-7.py
__author__ = 'howie'
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper():
print("%s was called" % func.__name__)
func()
print("bye~")
return wrapper
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello()
print(hello.__name__)
執(zhí)行代碼:
outputs:
hello was called
Hello howie!
bye~
hello
functions.wraps作用是不是一目了然哈到了這一步,再看01.py的代碼,是不是代碼結構清晰明了,只不過多了個參數
#!/usr/bin/env
# -*-coding:utf-8-*-
# script: 01.py
__author__ = 'howie'
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("%s was called" % func.__name__)
func(*args, **kwargs)
return wrapper
@decorator
def hello(name="howie"):
print("Hello %s!" % name)
hello('world')
猜都猜得到執(zhí)行后輸出什么了。
3.結語
只要了解裝飾器原理,不管是帶參數的裝飾器,還是裝飾器類,都是小菜一碟。
若有錯誤,盡請指出。