Python自学之路——装饰器的秘密
先不管装饰器到底是个什么东东,让我们来聊一聊函数的几个点吧。我们知道,在python里,一切皆是对象,那么函数呢,当然也是对象,而且人家还是一级对象呐。既然是对象,那它就可以被赋值给变量,反之,通过变量也能调用函数。好,需特别注意的点来了,函数调用一定是函数名 + (),如果没有这个括号,函数是不会被调用的,它只能是表示内存里的一个地址,看下面
1 def happy(): 2 print('be happy') 3 print (happy) #并没有调用happy函数,只是打印了happy的地址 4 happy() #调用happy函数
<function happy at 0x0000014B53C1F048>
be happy
我们再来看一下函数作为变量被赋值的例子
对于happy函数,如果我想在be happy这句前加个名字,该怎么办呢?理所当然的,我们会这样加
1 def happy(): 2 print('mumu') 3 print('be happy')
可是,我们有没有想过,这样的话我们就破环了原来happy这个函数,并且,如果需要修改的函数量大一点,难不成我们还得一个个进去改吗?这种场景肯定是不允许的,于是,我们又琢磨出了一种方法……
1 def happy(): 2 print('be happy') 3 def name(func): 4 print('mumu') 5 return func 6 happy = name(happy) 7 happy() 8 print(happy.__name__,name.__name__,)
mumu
be happy
happy name
有没有发现,上面代码中最神奇的一句就是 happy = name(happy) ,通过这句话,python将happy的地址传给了name函数,让name函数执行它的代码(即增加一个名字)然后返回happy的地址,最后调用happy。这样,我们在没有改变happy函数的前提下实现了需求,是不是很厉害呢?其实,这就是装饰器的秘密!
然而,人家既然是装饰器,肯定不会是这样和别的代码一样样的啊,看好了,人家的形式可是这样的呢 @+函数名 让我们看一下用了真正装饰器的代码长什么样吧
1 def name(func): 2 print('mumu') 3 return func 4 @name 5 def happy(): 6 print('be happy') 7 happy() 8 print(happy.__name__,name.__name__,)
千万不要以为装饰器是如此简单噢,上一个稍微难一点的例子吧,借这个例子让我们好好捋一下装饰器在python解释器的原理
我们再来看一下下面两个程序的区别
写到这里,是不是发现我们都还没有参数的装饰器呢,给函数加个参数让我们来看看
def name(func): def inner(arg): print('mumu') return func(arg) return inner @name def happy(user): print('%s be happy'% user) return "ye!" ret = happy('must') print('返回值:',ret)
mumu
must be happy
返回值: ye!
例子只是简单给了一个参数,如果你想要多个参数,甚至是多个不同类的参数,就用万能参数*args,**kwargs呗
现在我们应该明白,其实装饰器就是在原函数外面套了一层函数,使我们在调用原函数的时候调用的是装饰器返回给你的函数,那么,我们就想了,装饰器可以多个吗,一个一个地往函数上套?答案是肯定的,就看一下两个装饰器的例子吧,其实我觉得的吧,多个装饰器没必要,搞得那么复杂……
1 def outer1(func): 2 def inner(): 3 print ('o1,before') 4 func() 5 print ('o1,after') 6 return inner 7 8 def outer2(func): 9 def inner(): 10 print ('o2,before') 11 func() 12 print ('o2,after') 13 return inner 14 15 @outer2 16 @outer1 17 def happy(): 18 print ('happy everyday') 19 20 happy()
o2,before
o1,before
happy everyday
o1,after
o2,after
对于这种多个装饰器我们可以理解为它像俄罗斯套娃一样,最里面的原始函数被外面的装饰器函数一层一层地嵌套,原始函数的函数名传递给最靠近它的装饰器,里层的装饰器返回值会传递给外层装饰器的参数。