Decorator(装饰器)是在写Python代码的过程中,经常会被用到的一个语言特性,它可以大幅度减少重复的模板代码,并且,对于已有代码的重构往往也有奇效。但是,实现一个Decorator时的重重嵌套函数定义,经常让人头晕。下面就以一个常见的函数Cache装饰器作为例子,浅析Python中的装饰器特性。
Decorator简介
首先要注意的是,Python在引入Decorator时,其实并没有引入任何新的语言特性,因为Decorator只是一种“语法糖”,不使用@decorator这样的语法,也完全可以使用Python的原有语法实现Decorator的功能。这得益于Python中一切皆是对象。对于这样的一个decorator:
1 2 3 |
@deco def func(): pass |
也就相当于:
1 2 3 |
def func(): pass func = deco(func) |
这种写法就像对原程序打了一个Monkey Patch。
Deocrator基本应用
无参数Decorator
下面用一个缓存函数返回值的Decorator说明其最基本的实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# -*- coding: utf-8 -*- def func_cache(func): cache = {} def inner_deco(*args): if args in cache: print('func {} is already cached with arguments {}'.format( func.__name__, args)) return cache[args] else: print('func {} is not cached with arguments {}'.format( func.__name__, args)) res = func(*args) cache[args] = res return res return inner_deco @func_cache def add_two_number(a, b): return a + b if __name__ == "__main__": print('1. add_two_number(1, 2)') add_two_number(1, 2) print('2. add_two_number(2, 3)') add_two_number(2, 3) print('3. add_two_number(1, 2)') add_two_number(1, 2) |
其中,func_cache就是我们实现的Decorator,它以一个函数对象(func)作为参数,返回另一个函数对象(inner_deco),因此,当我们每次调用被func_cache装饰过的函数(add_two_number)时,调用的其实是inner_deco,也即:
1 |
add_two_number(1, 2) --> inner_deco(1, 2) |
在这里,可以给出Decorator的一个粗浅定义:Decorator是一个函数,它以一个函数对象A为参数,返回另一个函数对象B。对象B定义在Decorator体内,形成一个闭包。函数A和函数B接受的参数相同。每当程序调用函数A时,实际上会转换为对函数B的调用。
再看inner_deco,它内部实现的就是函数返回值缓存的逻辑,并打印了一些调试信息。
但是这里有一个明显的问题:inner_deco只能接受*arg,也就是列表参数,这就限制了这个Decorator的使用范围。下面这个版本就添加了**kwargs的支持。需要注意的是,kwargs不能进行hash,也就不能直接作为python中字典的key值,因此这里现将其转成一个frozenset。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# -*- coding: utf-8 -*- def func_cache(func): cache = {} def inner_deco(*args, **kwargs): key = (args, frozenset(kwargs.items())) if key if key Decorator简介首先要注意的是,Python在引入Decorator时,其实并没有引入任何新的语言特性,因为Decorator只是一种“语法糖”,不使用@decorator这样的语法,也完全可以使用Python的原有语法实现Decorator的功能。这得益于Python中一切皆是对象。对于这样的一个decorator:
也就相当于:
这种写法就像对原程序打了一个Monkey Patch。 Deocrator基本应用无参数Decorator下面用一个缓存函数返回值的Decorator说明其最基本的实现方式:
其中,func_cache就是我们实现的Decorator,它以一个函数对象(func)作为参数,返回另一个函数对象(inner_deco),因此,当我们每次调用被func_cache装饰过的函数(add_two_number)时,调用的其实是inner_deco,也即:
在这里,可以给出Decorator的一个粗浅定义:Decorator是一个函数,它以一个函数对象A为参数,返回另一个函数对象B。对象B定义在Decorator体内,形成一个闭包。函数A和函数B接受的参数相同。每当程序调用函数A时,实际上会转换为对函数B的调用。 再看inner_deco,它内部实现的就是函数返回值缓存的逻辑,并打印了一些调试信息。 但是这里有一个明显的问题:inner_deco只能接受*arg,也就是列表参数,这就限制了这个Decorator的使用范围。下面这个版本就添加了**kwargs的支持。需要注意的是,kwargs不能进行hash,也就不能直接作为python中字典的key值,因此这里现将其转成一个frozenset。
|