python decorators
装饰器基础
Decorator 本质
@ 本质是语法糖- Syntactic Sugar
使用@decorator 来修饰某个函数 func 时:
@decorator
def func():
pass
其解释器会解释成:
func = decorator(func)
注意这条语句会被执行
多重装饰器
@decorator_one
@decorator_two
def func():
pass
相当于:
func = decorator_one(decorator_two(func))
带参数装饰器
@decorator(arg1, arg2)
def func():
pass
相当于:
func = decorator(arg1,arg2)(func)
使用 *args、**kwargs
给被装饰函数传递参数
def wrapper(func):
def wrapper_in(*args, **kwargs):
# args是一个数组,kwargs一个字典
print("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper_in
@wrapper
def func(parameter1, parameter2, key1=1):
print("call func with {} {} {}".format(parameter1, parameter2, key1))
func("haha", None, key1=2)
# func is running
# call func with haha None 2
带参数的装饰器
def log(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
print("%s with warn is running" % func.__name__)
elif level == "info":
print("%s with info is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
return decorator
@log("warn")
def foo(*args, **kwargs):
print("args {}, kwargs{}".format(args, kwargs))
foo(1, 2, a = 3)
# foo with warn is running
# args (1, 2), kwargs{'a': 3}
等同于
def foo(name='foo'):
print("args {}, kwargs{}".format(args, kwargs))
foo = log("warn")(foo)
方法装饰器
类方法是一个特殊的函数,它的第一个参数 self 指向类实例
所以我们同样可以装饰类方法
def decorate(func):
def wrapper(self):
return "{0}
".format(func(self))
return wrapper
class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe"
@decorate
def get_fullname(self):
return self.name+" "+self.family
my_person = Person()
print my_person.get_fullname()
# John Doe
上例相当于固定了 self 参数,不太灵活
使用 *args, **kwargs
传递给 wrapper 更加通用:
def pecorate(func):
def wrapper(*args, **kwargs):
return "{0}
".format(func(*args, **kwargs))
return wrapper
class Person(object):
def __init__(self):
self.name = "John"
self.family = "Doe"
@pecorate
def get_fullname(self):
return self.name+" "+self.family
my_person = Person()
print my_person.get_fullname()
类装饰器
类实现 __call__
方法后变成可调用对象,故可以用类做装饰器
class EntryExit(object):
def __init__(self, f):
self.f = f
def __call__(self):
print "Entering", self.f.__name__
self.f()
print "Exited", self.f.__name__
@EntryExit
def func1():
print "inside func1()"
@EntryExit
def func2():
print "inside func2()"
def func3():
pass
print type(EntryExit(None))
# func1 变为类实例
print type(func1)
print type(EntryExit)
# func3 是普通函数
print type(func3)
func1()
func2()
#
#
#
#
# Entering func1
# inside func1()
# Exited func1
# Entering func2
# inside func2()
# Exited func2
类装饰器
@EntryExit
def func1():
print "inside func1()"
等同于
def func1():
print "inside func1()"
# 此处可以看出 func1 是类EntryExit的一个实例
func1 = EntryExit(myfunc1)
装饰器装饰类
register_handles = []
def route(url):
global register_handles
def register(handler):
register_handles.append((".*$", [(url, handler)]))
return handler
return register
@route("/index")
class Index():
def get(self, *args, **kwargs):
print("hi")
# Index 仍然为原来定义的类实例
# 相当于在定义类的同时调用装饰器函数 route, 将该类注册到全局路由 register_handles
@route("/main")
class Main():
def get(self, *args, **kwargs):
print("hi")
print (register_handles)
print(type(Index))
# [('.*$', [('/index', )]), ('.*$', [('/main', )])]
#
@route("/index")
class Index():
def get(self, *args, **kwargs):
print("hi")
Index = route("/index")(Index)
# register 返回传入的 handler,故 Index 仍然为类对象
functools
上述装饰器实现有个问题,就是被装饰函数的属性被改变