(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