Python装饰器

来源:互联网 发布:高达seed 知乎 编辑:程序博客网 时间:2024/04/24 20:29

一颗语法糖——装饰器
理论不去管,只管能办事:

1.  我要让一个函数在执行的时候,去做一些事情,比如,我要看看这些函数是不是有docstring,将这个功能拿出来,定义一个装饰器:
def showmedoc(func):
     if func.__doc__:
          print 'has doc'
     else:
          print 'no doc'
     return func                                      #别装饰了之后把人家原来的函数给盖了,除了需要的情况...
和装饰设计模式异曲同工,差不多就是一个东西,下面就用它来办事情 了:

@showmedoc
def f1():
    '''i have doc'''
    pass

@showmedoc
def f2():
     pass

执行结果:
>>> 
has doc 
no doc
>>> 

现在的情况是,我们在代码里并没有调用f1,f2,我们知识装饰了一下他们。却有输出,说明在执行的时候,解释器先扫描被装饰的函数,装饰好了,然后就等着函数执行了。

2.再看看这样的情况:

def say_hi():
    print 'hello'


def showmedoc(func):
    if func.__doc__:
        print 'has doc '
    else:
        print 'no doc'
    return say_hi

@showmedoc
def f1():
    '''hello world'''
    print "hihi"


f1()

得到输出:
>>> 
has doc 
hello
>>> 

很明显,我们改变了程序的入口!我们知道,第一行是在f1定义时,被装饰的时候输出的,以判断她是不是有docstring。然后呢,我们改变了程序的入口到say_hi。我们原来的函数实现跟我们说88了,取而代之的是say_hi。

3。根据上下文环境来改变程序的入口:

实际应用中,会遇到这样的情况:我们根据用户是否已经登陆来改变程序的执行流程。
用况:我们的某个处理过程需要用户的登陆信息,用户不登陆这个处理是没有办法继续的。那怎么去保证用户登陆呢?装饰一下,给函数加个身份验证的功能呗。我们来模拟以下这个装饰器:
def login_required(func):
      如果用户登陆:
             什么都不做
      如果用户没有登陆:
            重定向到用户登陆窗口
      return func

 现在好了,只要你的处理过程需要用户的登陆信息,你就可以简单地装饰一下就可以了,而不用在每个处理过程里去写重复的代码了:
@login_required
def func():
      处理

这样将公共的功能提取出来;增强了代码的可读性。

4.带参数的装饰器

默认情况下,我们装饰一个函数的时候,这个函数对象就默认的参数是函数对象本身。但是我们需要传递多个参数该怎么办呢??
想当然地我们来了一个
def showmedoc(func,x):
    if func.__doc__:
        print 'has doc '
    else:
        print 'no doc'
    return say_hi
其他的都不改变。
我想当然地来了一个,所以当然不行了,在
@showmedoc
def f1():
    '''hello world'''
    print "hihi"
中,showmedoc只能接受一个参数,就是f1。而我们定义的装饰器却要接受两个参数,,所以不行。how to do ???
very well 
我们再套一曾试试:
def showmedoc(x):
    def xshowmedoc(func):
        if func.__doc__:
            print 'has doc ' * x
        else:
            print 'no doc' * x
        return say_hi
    return xshowmedoc

@showmedoc(2,f1)
def f1():
    '''hello world'''
    print "hihi"

执行结果:
>>> 
has doc has doc 
>>> 

我纳闷了,这个func参数是什么时候溜近来的,在我的装饰器局部变量里根本没有什么func啊??what happened ?/
def showmedoc(x,y,z):
    def xshowmedoc(func):
        if func.__doc__:
            print 'has doc ' * x
        else:
            print 'no doc' * x
        return say_hi
    return xshowmedoc

@showmedoc(2,5,6)
def f1():
    '''hello world'''
    print "hihi"

变成这样后依然可以正常执行。KO,我们可以断定了,外面这一层衣服就一提供参数的东东,其他的什么也不是了。真正干活的是里面的xshowmedoc:

def showmedoc(x,y,z):
    def xshowmedoc(func,x):
        if func.__doc__:
            print 'has doc ' * x
        else:
            print 'no doc' * x
        return say_hi
    return xshowmedoc

@showmedoc(2,5,6)
def f1():
    '''hello world'''
    print "hihi"
我在xshowmedoc中加了一个参数:
>>> 
Traceback (most recent call last):
  File "F:/deco.py", line 58, in <module>
    @showmedoc(2,5,6)
TypeError: xshowmedoc() takes exactly 2 arguments (1 given)
>>> 
显然是错误的,很显然,我们没有直接为xshowmedoc提供任何参数,可是它却说 (1 given),事情很清楚了,函数对象知道你穿的那件衣服只是一层摆设,它是与装饰器本身相关的,而我要把自己以身相许给真正干活的那位!当然就是xshowmedoc。

def showmedoc():
    def xshowmedoc(func):
        if func.__doc__:
            print 'has doc ' 
        else:
            print 'no doc'
        return say_hi
    return xshowmedoc

@showmedoc()
def f1():
    '''hello world'''
    print "hihi"

这 样也是对的,由次可见,无参数的装饰器只是一个特例。而是不是有参数,主要看是怎么用 了,你在用装饰器时后面有括号,就是有参数的,没有括号就是没有参数的。没有参数的很简单,就直接将 装饰的函数对象作为第一个参数传递给了装饰器 。如果有参数的话(应用的时候有括号),解释器明白这个是个带参数的装饰器 ,然后呢它就会把这个被装饰的函数对象作为这件衣服下定义的所有的函数的 第一个参数传递进去!!,这样就可以对函数对象进行操作了。可以证明一下我们的想法:
def showmedoc():
    def xshowmedoc(func):
        if func.__doc__:
            print 'has doc ' 
        else:
            print 'no doc'
        return say_hi
    def xshowmedoc2(func):
        if func.__doc__:
            print 'has doc my god' 
        else:
            print 'no my god'        
    return xshowmedoc2

@showmedoc()
def f1():
    '''hello world'''
    print "hihi"

输出:
>>> 
has doc my god
>>> 

从这里我们又可以看出,你可以有不同的装饰方案,可以根据条件来设置!!够强大吧!最关键的一点就是究竟是有参数还是没有参数是从应用的角度看的,而不是从定义的角度看的。从定义的角度看 ,装饰器必须有一个参数——被修饰的函数。

my god,究竟是怎么发生的呢?有参数的装饰器真是太难理解了。

从应用的地方看:@showmedoc(),其中
showmedoc()是个函数的调用,它返回什么,我的天,它不就是个装饰器吗,似曾相识啊,就是个无参数的装饰器啊,就是下面这个东西啊:
    def xshowmedoc2(func):
        if func.__doc__:
            print 'has doc my god' 
        else:
            print 'no my god'  

转了一个大圈圈又回来了!!我们仅仅是定义了一个返回装饰器的函数,然后调用这个函数我们就得到一个装饰器。showmedoc()不是装饰器,而是它返回的那个东西才是装饰器,showmedoc()仅仅就是为了传递参数而已。
其实再多看 一点,showmedoc()是什么,showmedoc又是什么????在python里带括号的就是调用。而不带的呢,就是函数对象。而装饰器是一个什么玩意儿?是一个函数对象!

再多看点东西,装饰器这个东西只能接收一个参数<我自己的理解 >