- 装饰器(decorator)的本质:函数闭包(function closure)的语法糖(syntactic sugar)
- 函数闭包:一个函数,其参数和返回值都是函数
- 用于增强函数功能
- 面向切面编程(AOP)
- 语法糖:指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用
- 语法糖没有增加新功能,只是一种更便捷的写法
- 语法糖可以完全等价的转换为原本非语法糖的代码
- 装饰器:本质是闭包
-
简单的类型检查
def requires_ints(decorated): '''test''' def inner(*args, **kwargs): kwargs_values = [i for i in kwargs.values()] for arg in list(args) + kwargs_values: if not isinstance(arg, int): raise TypeError("%s only accepts integers as arguments."%decorated.__name__) return decorated(*args, **kwargs) return inner @requires_ints def foo(x, y): '''return the sum of x and y''' return x + y # print(help(foo)) # print(foo(3, 5))
-
保存帮助信息: 保留原本注释
import functools def requires_ints(decorated): @functools.wraps(decorated) def inner(*args, **kwargs): kwargs_values = [i for i in kwargs.values()] for arg in list(args) + kwargs_values: if not isinstance(arg, int): raise TypeError("%s only accepts integers as arguments."%decorated.__name__) return decorated(*args, **kwargs) return inner @requires_ints def foo(x, y): '''return the sum of x and y''' return x + y print(help(foo)) print(foo(3, 5))
-
用户验证:验证用户是否已经登录程序
class User: def __init__(self, username, email): self.username = username self.email = email class AnonymousUser(User): def __init__(self): self.username = None self.email = None def __nonzero__(self): return False import functools def requires_user(decorated): @functools.wraps(decorated) def inner(user, *args, **kwargs): if user and isinstance(user, User): return decorated(user, *args, **kwargs) else: raise ValueError("A valid user us required to run this") return inner
-
格式化输出
import functools import json def json_output(decorated): @functools.wraps(decorated) def inner(*args, **kwargs): result = decorated(*args, **kwargs) return json.dumps(result) return inner @json_output def do_nothing(): return {"status": "Done"} print(do_nothing())
-
捕获特定异常,并以JSON输出
import functools import json class JSONOuptError(Exception): def __init__(self, message): self._message = message def __str__(self): return self._message def json_output(decorated): def inner(*args, **kwargs): try: result = decorated(*args, **kwargs) except JSONOuptError as ex: print(ex) result = { "status": "error", "message": str(ex), } return json.dumps(result) return inner @json_output def error(): raise JSONOuptError(message="Lovy") print(error())
-
-
日志管理
import functools import logging import time def logged(method): @functools.wraps(method) def inner(*args, **kwargs): start = time.time() return_value = method(*args, **kwargs) end = time.time() delta = end - start logger = logging.getLogger("decorator.logged") logger.warn("Called method %s at %.02f; execution time %.02f seconds;result %r." %(method.__name__, start, end, delta)) return return_value return inner @logged def sleep_and_return(return_value): time.sleep(2) return return_value print(sleep_and_return(10))
-
装饰器参数
import functools import json class JSONOutputError(Exception): def __init__(self, message): self._message = message def __str__(self): return self._message def json_output(indent = None, sort_key = False): '''return Json string''' def actual_decorator(decorated): @functools.wraps(decorated) def inner(*args, **kwargs): try: result = decorated(*args, **kwargs) except JSONOutputError as ex: result = { "status":"error", "message":str(ex) } return json.dumps(result, indent=indent, sort_keys=sort_key) return inner return actual_decorator @json_output(indent=4) def do_something(): return {"status":"ok"} print(do_something()) @json_output() # 调用签名 def do_something(): return {"status":"ok"} print(do_something())
import functools import json class JSONOutputError(Exception): def __init__(self, message): self._message = message def __str__(self): return self._message def json_output(decorated_ = None, indent=None, sort_keys=False): # print(indent or sort_keys) # print(decorated_) if decorated_ and (indent or sort_keys): # print(1) raise RuntimeError("Unexpected arguments.") def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): try: result = func(*args, **kwargs) except JSONOutputError as ex: result = { "status":"error", "message":str(ex) } return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner if decorated_: return actual_decorator(decorated_) else: return actual_decorator ''' 没有方法签名的纯装饰器调用; 首先会解析 actual_decorator(decorated_) 然后以inner作为唯一参数调用该函数的返回结果 ''' @json_output def do_something(): return {"status":"ok"} print(do_something()) ''' 带有参数的关键字调用,函数必须返回一个实际装饰器 该装饰器接受被装饰的方法返回inner函数 该函数直接返回actual_decorator装饰器 ''' @json_output() def do_something(): return {"status":"ok"} print(do_something()) @json_output(indent=4) def do_something(): return {"status":"ok"} print(do_something())
-
装饰类
import functools import time def sortable_by_creation_time(cls): # 获取类的初始化方法 original_init = cls.__init__ # 装饰原初始化方法 @functools.wraps(original_init) def new_init(self, *args, **kwargs): original_init(self, *args, **kwargs) self._created = time.time() cls.__init__ = new_init cls.__lt__ = lambda self, other : self._created < other._created cls.__gt__ = lambda self, other : self._created > other._created return cls @sortable_by_creation_time class Sortable: def __init__(self, identifier): self.identifier = identifier def __repr__(self): return self.identifier first = Sortable('first') second = Sortable("second") third = Sortable("third") sortables = [first, second, third] print(sorted(sortables))
-
类型转换
class Task: def __call__(self, *args, **kwargs): return self.run(*args, **kwargs) def run(self, *args, **kwargs): raise NotImplementedError("Subclasses must implement `run`") def identify(self): return 'I am a task' def task(decorated): class TaskSubclass(Task): def run(self, *args, **kwargs): return decorated(*args, **kwargs) # return TaskSubclass return TaskSubclass() # 增加回调功能 @task def foo(): return 2 + 2 # print(foo().run()) print(foo()) print(foo.run()) print(foo.identify())