Python中的装饰器
虽然可以通过元组实现相关的功能,但是因为实际中的使用需求,最终实现了装饰器。
刚开始python仅仅支持@staticmethod一类的装饰器,后来才觉得应该广泛地支持decorator,@这个符号一开始参考自java annotation,Barry Warsaw 称之为pie-decorator,就像python的第一个音节,而且@看起来也像一个派。
其实decorator这个名字常常被人诟病,因为它包含了太多含义,比如在编译领域表示一个已经被标记的语法树。设计之初考虑了很多内容比如好看好记易用使用等等,考虑了很多用例,总之得出现在的用法是很科学的。
现在的用法:
函数装饰器
顺序
多个decorator使用就近原则,这个来自于数学符号的习惯。
语法
在一封邮件里Guido提出了他对@decorator限制声明形式的看法
后来支持了
@decomaker(argA, argB, ...) def func(arg1, arg2, ...): pass
相当于
func = decomaker(argA, argB, ...)(func)
讨论于邮件
位置
decorator也经过很多讨论才得到今天的形式
示例
1.让一个function在退出时被执行
def onexit(f): import atexit atexit.register(f) return f @onexit def func(): ...
2.singleton instance
def singleton(cls): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance @singleton class MyClass: ...
3.向函数添加属性
def attrs(**kwds): def decorate(f): for k in kwds: setattr(f, k, kwds[k]) return f return decorate @attrs(versionadded="2.2", author="Guido van Rossum") def mymethod(f): ...
4.检查函数接受参数和返回值的类型
def accepts(*types): def check_accepts(f): assert len(types) == f.func_code.co_argcount def new_f(*args, **kwds): for (a, t) in zip(args, types): assert isinstance(a, t), \ "arg %r does not match %s" % (a,t) return f(*args, **kwds) new_f.func_name = f.func_name return new_f return check_accepts def returns(rtype): def check_returns(f): def new_f(*args, **kwds): result = f(*args, **kwds) assert isinstance(result, rtype), \ "return value %r does not match %s" % (result,rtype) return result new_f.func_name = f.func_name return new_f return check_returns @accepts(int, (int,float)) @returns((int,float)) def func(arg1, arg2): return arg1 * arg2
5.为一个类实现一系列特定的接口
def provides(*interfaces): """ An actual, working, implementation of provides for the current implementation of PyProtocols. Not particularly important for the PEP text. """ def provides(typ): declareImplementation(typ, instancesProvide=interfaces) return typ return provides class IBar(Interface): """Declare something about IBar here""" @provides(IBar) class Foo(object): """Implement something here..."""
参考:https://www.python.org/dev/peps/pep-0318/
类装饰器
metaclass会被继承,而装饰器不会被继承,所以我们实现了类装饰器来应对对单个类的特定使用。唯一的不同之处是一个作用于类,一个作用于函数。
另一篇参考:http://thecodeship.com/patterns/guide-to-python-function-decorators/
内容比其他教程没太多变化,主要是看懂了
def p_decorate(func): def func_wrapper(name): return "<p>{0}</p>".format(func(name)) return func_wrapper def strong_decorate(func): def func_wrapper(name): return "<strong>{0}</strong>".format(func(name)) return func_wrapper def div_decorate(func): def func_wrapper(name): return "<div>{0}</div>".format(func(name)) return func_wrapper 定义的三个装饰器作用到一个函数上 @div_decorate @p_decorate @strong_decorate def get_text(name): return "lorem ipsum, {0} dolor sit amet".format(name) print get_text("John") # Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>
不如写成
def tags(tag_name): def tags_decorator(func): def func_wrapper(name): return "<{0}>{1}</{0}>".format(tag_name, func(name)) return func_wrapper return tags_decorator @tags("p") def get_text(name): return "Hello "+name print get_text("John") # Outputs <p>Hello John</p>
func_wrapper就相当于“函数的(标签)包装纸”,tags_decorator相当于"标签的包装工",tags就是一个“包装工的生成器”。
另一部分内容就是老生常谈的__name__ __doc__ __module__会被最接近func的wrapper给替换,为此使用@functools.wraps(func)夹在decorator和wrapper之间,python就会帮你复制一下。
大概像这样
def tags(tag_name): def tags_decorator(func): @functools.wraps(func) def func_wrapper(name): return "<{0}>{1}</{0}>".format(tag_name, func(name)) return func_wrapper return tags_decorator