(5年前写的,贴在这里方便回看)
Python代码里经常看到一个符号@摆在函数的上面一行,这叫decorator,但它到底是什么,表示什么意思,有什么好处呢?decorator是用来修饰函数的,等于将原函数(比如foo)经过了一定加工,达成的效果是foo = our_decorator(foo)这个样子,可以简写成@our_decorator在foo上面。
举个简单的例子:首先我们定义一个函数
1 | |
调用此函数
1 | |
加上decorator:
1 | |
我们将foo作为一个function object传输给了our_decorator,our_decorator被调用并返回了function wrapper这个函数。这等于说foo经过了our_decorator的修饰,多了一些功能。但是这时候foo还没有被调用。接下来调用被修饰过的foo函数:
1 | |
这就是decorator做的事情,我们可以用一个@符号来取代foo = our_decorator(foo)这一行,更加简单和pythonic:
1 | |
其它的装饰函数定义和调用foo函数的方法不变。
要理解decorator,主要是要理解callback function的含义,即给现有函数传入一个函数,返回的也是一个函数,而传入函数将作为一个值暂居在被返回的函数里,等到现有函数被调用的时候,实际上调用的是被返回的那个函数,其中也包括了我们传入的函数。
稍微复杂一点,如果我们想给decorator也加parameter的话:
1 | |
在装饰函数的时候,尽管我们最后调用的还是原来那个函数的名字foo,但是它的元信息已经发生了变化,比如:
1 | |
我们看到,原函数被传入装饰函数时,还具有原来的信息,但是当我们调用被装饰过的函数时,函数名字、docstring、被调用的模块都发生了变化。如果我们想保留foo原来的元函数信息的话,可以在装饰函数中进行元函数信息的保留,即将返回函数的元信息设置成原函数的元信息:
1 | |
哎哟,这么多行累死了是不是?我们可以给 function_wrapper这个函数也加一个装饰函数,达到一样的效果:
1 | |
举一个现实生活中的例子。用户登录页面的时候,我们常要先检查其是否有访问权限,因为这种操作非常多,可以将之refactor成一个装饰函数:
1 | |
我们也可以用一连串的decorator,比如以下用了两个:
1 | |
最后,任何callable object都可以成为装饰主体,因此除了函数,我们还可以用class。如:
1 | |
当进行装饰的时候,__init__被调用并保存了f的信息,当foo被调用的时候,__call__函数被调用。
在现实生活中常用的decorator有:
@classmethod (俺尚不是很理解它的应用价值)
在一个类的函数上使用@classmethod,函数返回该类的构造函数。这样的情况下,我们调用了一个类中的函数,同时也创造了一个对象。比如:
1 | |
@classmethod比@staticmethod多的一个好处是,它能正确反映继承关系。如:
1 | |
@property
在类里,我们常用一个getter和setter来对类中的变量进行操作,这是为了data encapsulation的考虑,也可以在set和get的时候进行bound check。@property就提供了这样一个方法。
1 | |
我们可以看到,full_name函数经过@property装饰,有了getter的功能,而经过@full_name.setter的装饰,有了setter的功能。虽然在类中,函数不可以有相同的名字,但他们经过了装饰,所以这是可行的。要注意的是,getter的装饰函数叫@property,setter的装饰函数是@func.setter。property函数是这样定义的:property(fget=None, fset=None, fdel=None, doc=None)。装饰之后,我们得到:
1 | |
之后我们在这个类的对象上进行set或者get的操作的话,就会调用相应的函数。比如person.name = ‘Bob’,那么就会调用full_name_getter函数。但要注意的是,只有当类从object继承的时候,才会有这样的效果。所以在定义类的时候,一定要从object继承哦。
####Reference