python decorator的理解

本文详细介绍了Python中装饰器的概念及其实现方式,包括基于函数和基于类的定义方法,并探讨了其与装饰器模式和AOP的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

    每次我们看到decorator的时候,如果有java背景的人会潜意识的想到decorator pattern。的确,decorator pattern是一种将一些功能叠加到一个类上的一种手法。在java里我们需要费一些手脚才能把这些东西给叠加起来。那么在python里面呢?decorator的作用和意义仅仅是在于这个pattern么?我们可以来详细的看看。

 

python decorator的定义和应用

    在python里定义decorator主要有两种方式,一种是基于方法的方式,一种是基于类定义的方式,他们定义的形式稍微有一些不同,不过都基于同样的思想。在看decorator的具体定义之前,我们先看看python里对待方法的处理方式。

函数的定义和传递

    我们来看下面的代码:

def outer():
    def inner():
        print("Inside inner function.")
    return inner

    这部分代码比较简单,就是在一个outer的方法里定义了一个inner的方法。然后比较奇怪的是,outer方法返回的是inner方法。这就是一个看来很难理解的地方。在python里,方法/函数都可以作为一个类似于普通对象参数进行传递。这是很多函数式编程语言里常用的思想,在后续的文章里还会进一步讨论。所以这里outer方法里定义了inner方法,然后它返回了inner方法。那么我们该怎么使用它们呢?

    这是使用这些代码的典型交互结果:

>>> from outer import outer
>>> foo = outer()
>>> foo()
Inside inner function.

     这里,我们将outer()方法的结果作为一个对象赋值给foo。按照前面的理解,实际上就是inner方法。然后我们用foo()这样的方式调用了inner方法。

    前面的这个示例说明了我们可以将函数作为一个对象当作返回结果。实际上,我们也可以将函数作为方法的参数来传递,我们再看另外一个示例:

def outer(func):
    def inner():
        print("Inner function before func execution.")
        return func()
    return inner

   这里outer方法传进来了一个func,在inner方法里我们执行了func()。可以说,这个func必须是一个可以执行的对象,比如实现__call__()方法的对象或者就是一个方法。

    我们该怎么使用前面这段代码呢?我们看下面这部分:

>>> def counter():
...     print("counter method execution")
... 
>>> foo = outer(counter)
>>> foo()
Inner function before func execution.
counter method execution

    这里我们定义了一个counter方法。然后将counter方法作为参数传递给outer方法。然后我们可以将outer方法调用的结果再赋值给foo,然后再把foo当作一个方法来调用。

    嗯,经过这一通折腾,我们就知道了在python里,可以定义一个函数,然后将它当成一个普通的对象参数来传递或者返回。这个参数在使用的时候就和调用普通方法一样。这种手法看起来比较怪,不过还是好理解。这些东西和decorator有什么关系呢?其实有了这一步,我们基于函数的方式来定义decorator就只差一点了。

基于函数的定义

    在前面那一部分,我们后面定义的示例里将一个函数传递到一个函数里,然后在inner()方法里做了一些包装和其他操作。这个过程不就是decorator所期望达到的效果吗?确实,在将函数作为参数这样传递的时候,我们无意间已经做到这个包装效果了。不过在python里,有一个更加典型的用法。我们还是以前面的示例为基础,假定我们已经定义好了outer方法和inner方法,我们将counter方法定义写成如下的方式:

>>> @outer
... def counter():
...     print("counter method execution")

    然后我们再用这样的方式来执行counter方法:

>>> counter()
Inner function before func execution.
counter method execution

     很奇怪,居然达到了一个同样的效果。实际上,我们的代码和前面的区别就在于加了一个@outer的修饰在方法定义上。问题的根源也就在于这里。这个@outer的修饰就相当于一个语法糖,我们执行counter()方法的时候实际上相当于变成了outer(counter)()。

    我们来看基于函数的方式来定义的decorator。他们几乎有一个最外层的函数,这个函数相当于一个中介一样,它本身一般不做什么,通常只是用来返回内部的函数。当然,在一些特殊的情况下它可能会传递一些参数。然后内部的那个方法可以访问通过外部方法传递进来的参数,这些参数里通常就包含有需要包装的函数或者对象了。在这个内部方法里除了调用传进来的函数参数,我们也可以做一些自己定义的东西。这样,我们定义的函数式decorator就是这么回事了。

 

基于类的定义

    看完前面基于函数的定义,我们再来看看基于类的定义。也许很多习惯了定义类的思路会更喜欢这种选择。不过确实,在实现上,基于类定义的方式显得更加直观和强大一些。我们先以前面的示例为基础,假设我们要定义一个outer的decorator类,那么我们该怎么做呢?

class outer(object):

    def __init__(self, func):
        self.func = func

    def __call__(self):
        print("Inner function before func execution.")
        return self.func()

    这里我们将outer定义成一个类,然后我们需要传递的函数作为一个参数传递给__init__方法。然后原来inner方法里实现包装操作的方法我们放到__call__方法里。这样,我们按照原来的方式来使用这部分代码,执行结果如下:

 

>>> from outer import outer
>>> @outer
... def counter():
...     print("counter method execution")
... 
>>> counter()
Inner function before func execution.
counter method execution

    看了基于类方式定义decorator的方式。那么这种方式和基于函数定义的方式比起来,有什么好处呢?其实好处主要有这么些个。一个是这里将需要包装的函数作为参数传递进来,在传递过来时通过__init__方法来专门负责封装。而__call__方法来专门负责增加自定义的特性。这样两者的分离使得管理更加清晰。而且如果我们需要增加额外的属性也比较好管理。

 

和decorator pattern以及aop的关系

    看了前面的示例,我们会发现python里的decorator其实可以做到好几个事。一个是decorator pattern。在我的这篇文章里讨论了decorator pattern。而decorator pattern我们知道,需要对一个对象的某些属性增加一些方法或者属性,所以我们要构造一个可以不断叠加属性进去的机制。而在decorator里,我们可以针对每个需要叠加的特性,定义好专门的decorator,然后再将需要被叠加的对象传递进来就可以了。和java的实现比起来显得更加简单直接。

    decorator和java里的aop看起来也有很强的关系。在前面的示例里,我们可以在某个方法被调用前或者调用后执行某些代码。只要我们将这个decorator修饰加到目的对象上就可以了。如果在java里实现类似的功能呢?我们发现本身语言的支持还是比较困难的,比如说我们要实现一个annotation,然后还要用反射去处理。或者用专门的工具就为了操作byte code。所以说在java里比较困难的地方在python这里反而很简单了。

 

总结

     Decorator在python里是一个基本的语言特性,使用它能够达到decorator pattern或者aop等效果。从更深的层次来说,它无非就是一个函数对另外一个函数对象的封装和传递。感觉和函数式编程语言里的curry化以及高阶函数有比较深的关系。后面有机会针对这两个点再深入的讨论讨论。

 

参考材料

http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/

http://www.artima.com/weblogs/viewpost.jsp?thread=240808

https://wiki.python.org/moin/PythonDecoratorLibrary

http://www.jeffknupp.com/blog/2013/11/29/improve-your-python-decorators-explained/

http://stackoverflow.com/questions/20945366/python-decorators

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值