`
frank-liu
  • 浏览: 1661480 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

python decorator的理解

阅读更多

简介

    每次我们看到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

分享到:
评论

相关推荐

    python self,cls,decorator的理解

    1. self, cls 不是关键字 在python里面,self, cls 不是关键字,完全可以使用自己写的任意变量代替实现一样的效果 代码1 复制代码 代码如下:class MyTest: myname = ‘peter’ def sayhello(hello): print “say ...

    深入了解和应用Python 装饰器 @decorator

    Python的装饰器(decorator)是一个很棒的机制,也是熟练运用Python的必杀技之一。装饰器,顾名思义,就是用来装饰的,它装饰的是一个函数,保持被装饰函数的原有功能,再装饰上(添油加醋)一些其它功能,并返回带...

    Python装饰器(decorator)定义与用法详解

    本文实例讲述了Python装饰器(decorator)定义与用法。分享给大家供大家参考,具体如下: 什么是装饰器(decorator) 简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回...

    Python实现Singleton模式的方式详解

    一方面可以加深对python的理解,另一方面可以更加深入的了解该模式,以便实际工作中能更加灵活的使用单例设计模式。 本文将介绍常见的实现单例模式的几种方式,这里暂不考虑多线程的情况。 为了准备该篇博文,之前写...

    深入理解Python装饰器

    装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它...

    深入理解Python中装饰器的用法

    装饰器的使用是Python的高级技巧之一,通过Decorator装饰器能够带来函数式编程中所需的很多特性,这里我们就来跟随文章一同深入理解Python中装饰器的用法,需要的朋友可以参考下

    解密Python中的描述符(descriptor)

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解。这些特性包括列表/集合/字典推导式,属性(property)、以及装饰器(decorator)。对于大部分特性来说,这些“中级”的语言特性有着完善的文档,...

    Python 的描述符 descriptor详解

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解。这些特性包括列表/集合/字典推导式,属性(property)、以及装饰器(decorator)。对于大部分特性来说,这些“中级”的语言特性有着完善的文档,...

    装饰器(Decorator):从字面上理解,就是装饰对象的器件。

    111

    python3-in-one-pic

    while break并continue 迭代器和发电机 理解力 功能 定义 争论 拉姆达 文献资料 @decorator 班级(OOP) class __init__()和self 实例 遗产 覆写 模块 import 搜索路径 包裹Pythonic标准库 os, sys datetime捐款...

    老生常谈Python进阶之装饰器

    要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,...

    Python中用Descriptor实现类级属性(Property)详解

    上篇文章简单介绍了python中描述器(Descriptor)的概念和使用,有心的同学估计已经Get√了该技能。本篇文章通过一个Descriptor的使用场景再次给出一个案例,让不了解情况的同学可以更容易理解。 先说说decorator 这...

    Python带参数的装饰器运行原理解析

    关于装饰器的理解,特别像《盗梦空间》中的进入梦境和从梦境出来的过程,一层一层的深入梦境,然后又一层一层的返回,被带入梦境的是被装饰的函数,装饰器就是使人入梦的工具。 上代码: from functools import ...

Global site tag (gtag.js) - Google Analytics