python装饰器
什么是装饰器?
装饰器的数学模型是复合函数 :
(f * g) (x) = f(g(x))
下面是摘自tornado中的一段代码:
class ComposeHandler(BaseHandler):
@administrator
def get(self):
key = self.get_argument("key", None)
entry = Entry.get(key) if key else None
self.render("compose.html", entry=entry)
administrator 是一个装饰器,在给服务器发出get请求,路由到这个ComposeHandler的时候,需要先运行administrator的逻辑。这个类似Java中的拦截器语法(AOP)。
administrator也是一个函数,它的样子是这样的:
def administrator(method):
"""Decorate with this method to restrict to site admins."""
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if not self.current_user:
if self.request.method == "GET":
self.redirect(self.get_login_url())
return
raise tornado.web.HTTPError(403)
elif not self.current_user.administrator:
if self.request.method == "GET":
self.redirect("/")
return
raise tornado.web.HTTPError(403)
else:
return method(self, *args, **kwargs)
return wrapper
这里使用了一个闭包函数wrapper(可调用的对象),它在函数内部被定义,当返回给上层调用者时,这个wrapper会在get函数调用之前运行起来,从而起到装饰的作用。回想一下,装饰模式的表述:用对客户透明的方式给一个对象动态地增加功能。python的装饰器展示的也是这种思想,但是python的装饰器强调的更多还是AOP得概念,用简洁的@语法抽象出公共功能,避免了一段“公共的客户代码”散落在代码各处的毛病,避免了维护的困难。
由于装饰器可以重叠装饰,所以它也符合了“动态增加功能的特点”,所以我把它理解成装饰模式的一种应用。
总结一下python装饰器的要点:
1、装饰器是一种特殊的函数
2、装饰器的参数至少有一个是函数对象
3、装饰器通常会返回一个函数对象
函数式的装饰器例子:
假想我们要给一个模块里的函数都加上追踪调试信息,函数进入时输出一段时间戳。使用装饰器,先定一个打印调试追踪信息的装饰器。
def log_deco(method):
def wrapper():
print time.strftime("%Y-%m-%d %H:%M:%S"),
print "Call funcion : %s" % method.__name__
return method() # method在这里调用 也可以不写return
return wrapper
@log_deco
def func1():
print "Processing something..."
print "Calculating something ..."
@log_deco
def func2():
print "Processing something2..."
print "Calculating something2..."
if __name__ == '__main__':
func1()
func2()
样例输出:
2017-09-19 11:20:41 Call funcion : func1
Processing something...
Calculating something ...
2017-09-19 11:20:41 Call funcion : func2
Processing something2...
Calculating something2...
内建装饰器 classmethod和staticmethod
classmethod和staticmethod是python的内建方法。它们是都是类。
在构造器中接受一个函数对象作参数,并返回一个函数对象的实例。
怎么在python里面写静态方法?函数声明的前面用 @staticmethod装饰一下就行。
class Foo:
@staticmethod
def func1():
print("demo func1..")
调用时可以直接用类名访问,或者实例访问也行。静态方法是一个与实例无关的方法,和Java、C++的静态方法是一样的概念。
python中的类方法和静态方法有什么区别?
1、静态方法的概念和Java、C++一样,它不和类、类的实例绑定,通过类名调用,也可通过实例调用,结果都是一样的;
2、类方法有一个cls的实参传入,代表与方法绑定的类对象;类方法与类绑定,而一般的方法是与实例绑定。类方法可以通过类名调用,也可以通过类的实例调用。
装饰器的其他写法
使用可调用对象做装饰器
类的 __call__方法被定义时,这个类的实例也是可被调用的。
什么意思,请看例子:
class CallDemo:
def __init__(self):
print "This is a class for callable object"
def __call__(self, *args):
print("Call ... run here ...")
return 100
c = CallDemo()
print(c()) # 实例调用
如果没有 重写 __call__方法 ,实例调用 c( ) 会导致解释器会报告一个AttributeError: CallDemo instance has no __call__ method 的错误
借助 __call__ ,写一个类装饰器, 为我的函数盖一个戳子:
class sign:
def __init__(self, func):
self.func = func
def __call__(self, *args):
print "== Dong hua's func =="
self.func()
print "== leave func == "
@sign
def foo():
print ("... This is foo function ...")
foo()
这里的类装饰器,需要在构造器上接收一个函数对象做参数,并重新定义 __call__函数。当调用foo()函数的时候,相当于
f = sign(foo)
f() #用类的实例调用,所以触发了 __call__的逻辑