python装饰器一个栗子说明

版权声明: https://blog.csdn.net/dashoumeixi/article/details/80487874

看装饰器前,必须理解闭包,闭包的解释在另一篇文章里

一句话解释:装饰器就是一个函数

先看一个例子:

推导式:testFunc = dec(testFunc)  #有C函数指针基础的比较好理解

def dec(func):
    def innerFunc(*args,**kwargs):
        print("原来的print语句调用不了了,哇哈哈")
    return innerFunc
def testFunc(name):
    print("hello:" + name)
testFunc = dec(testFunc)
testFunc("nihao")

dec 是一个普通函数, 参数:func是1个函数对象

内部又定义了个函数,可以随便定义N个函数

最后返回了内部的函数innerFunc

testFunc = dec(testFunc)

这行代码的意思是把testFunc 指向了innerFunc

所以现在调用testFunc 相当于调用了innerFunc

问题来了,参数呢,原来testFunc的参数name ,现在被替换成了 *args, **kwargs ,随便传入什么参数都可以了

把上面的方式用装饰器的方式来写一遍 : 

def dec(func):
    def innerFunc(*args,**kwargs):
        print("原来的print语句调用不了了,哇哈哈")
    return innerFunc
@dec
def testFunc(name):
    print("hello:" + name)
testFunc("nihao")

在需要被装饰的函数上方添加一个@,后面跟着装饰器.

装饰器是什么?

一个接受*函数对象*,并且返回一个*函数对象*,用于改变原函数的行为(不修改原代码;类似于java中的AOP)

当调用了testFunc() ,实际上是调用了innerFunc().

@dec
def testFunc(name) #相当于testFunc=dec(testFunc)

把innerFunc做些修改

def dec(func):
    def innerFunc(*args,**kwargs):
        print("args:",args)
        func(*args)
    return innerFunc

由于闭包的作用域,func 参数被存放在innerFunc.__closure__中的单元对象中 ,所以可以被调用

输出

testFunc("nihao")
#args: ('nihao',)
#hello:nihao

至此,装饰器基础解释完毕


深一些:

装饰器何时被调用 ? 作为模块导入时就会被调用,例子

def dec_again(func):
    print("dec_agin")
    def innerFunc(*args, **kwargs):
        print("dec_again 中的 innerFunc , 参数func:",func)
    return innerFunc
def dec(func):
    print("dec")
    def innerFunc(*args,**kwargs):
        print("dec 中的 innerFunc, 参数func:",func)
    return innerFunc
@dec_again
@dec
def testFunc(name):
    print("hello:" + name)
#注意没有调用testFunc(), 输出
#dec
#dec_agin

这个栗子说明了2个问题:

1.装饰器一开始就会调用,说的大白话一些是因为需要确定函数的执行流程

2.装饰器是可以叠加的,离testFunc最近的装饰器(@dec)先执行,然后@dec_again

之前说过了, testFunc = dec(testFunc)

那么这个栗子由此可以推倒:

testFunc = dec_again(dec(testFunc))

把dec(testFunc)的返回值作为参数传递给dec_again;

testFunc("hi")
#输出
#dec_again 中的 innerFunc , 参数func: <function dec.<locals>.innerFunc at 0x000000000279CD08>

看到了吗 ,当调用testFunc() 实际调用了,dec_again中的innerFunc(),参数是dec返回的innerFunc


接下来说一下,含有参数的装饰器(充分的利用了闭包):

这里的推导式变了,

没有参数的推导式:testFunc = dec(testFunc)

有参数的推导式:testFunc =  dec_factory(1,2,3)(testFunc),往下看

def dec_factory(*arg):
    print("arg:",arg)
    def dec(func): #这个才是装饰器函数, 接受1个函数对象
        def innerFunc(*args,**kwargs):
            print("我才是真正被执行的函数")
        return innerFunc #返回innerFunc , 替换了原来的testFunc
    return dec          #为什么要返回dec ?
@dec_factory(1,2,3) #需要调用 ,为什么?
def testFunc(name):
    print("hello:" + name)
#输出了arg: (1, 2, 3) ,注意没有调用testFunc()


一步步解释:

当有需要有参数的装饰器时 ,需要外面再'包裹'一层函数来接受参数.

前面说了装饰器函数接受一个函数对象,返回一个函数对象,用于改变原函数的行为或流程

所以可以把dec_factory看成一个装饰器工厂,专门用来返回装饰器函数的

这些参数由于闭包的作用域可以随意访问。

@dec_factory(1,2,3) #需要调用 ,为什么?
def testFunc(name):

没有参数时 , @dec 即可。这里为什么需要调用 .

dec_factory 本身并不是装饰器函数,而是一个'工厂'(用来接收参数 ),

 他返回的是一个装饰器函数(dec) ,这就是为什么需要调用

@dec_factory(1,2,3) #调用之后返回dec,相当于@dec
def testFunc(name):

进一步推导: testFunc = dec(testFunc)

至此,带有参数的装饰器解释完毕;

最后写一个混合使用的例子:

def print_factory(when):#when 在'before' 'after'中选一个
    #普通的内部函数
    def print_now(func,*args,**kwargs):
        print("print_now-> function , args,kwargs:",
              func,args,kwargs)
    #一个装饰器函数,在真正执行函数前打印一下
    def print_something_first(func):
        def innerFunc(*args,**kwargs):
            print("innerFunc , 我先打印然后再执行func的调用")
            print_now(func,*args,**kwargs)
            return func(*args,**kwargs)
        return innerFunc
    #又一个装饰器函数,在执行函数后打印一下
    def print_something_finnaly(func):
        def innerFunc(*args,**kwargs):
            try:
                return func(*args,**kwargs)
            finally:
                print_now(func,*args,**kwargs)
            return func(*args,**kwargs)
        return innerFunc
    try:
        return {"before":print_something_first , "after":print_something_finnaly}[when]
    except:
        raise ValueError("参数:'before'或者'after'")
@print_factory("after")
def echo_func(name):
    print("echo_func :" ,name)
echo_func("我是谁?")



猜你喜欢

转载自blog.csdn.net/dashoumeixi/article/details/80487874
今日推荐