python装饰器详解,多层装饰器,及带参数的装饰器。


pyhon学习有一段时间了,今天又碰到了Django的中间件,其实Django里面的中间件,就是用了多层的装饰器,然后去了解了一下多层装饰器,感觉有写东西差不多快忘了,也可能前面没学好。

现在重新记录,学习下。

 

普通装饰器

def warp(func):
    print('我是装饰器,碰到需要装饰的函数,一开始执行这里')
    def inner(*args, **kwargs):
        print('这里才是真正的装饰开始!')
        res = func(*args, **kwargs)
        print('装饰结束')
        return res
    print('我这里是外围,先出去了,里面的等需要的时候执行')
    return inner

@warp   # 装饰器符号


def demo(x, y):
    return x + y

if __name__ == '__main__':
    demo(1, 2)

 

运行结果:


我是装饰器,碰到需要装饰的函数,一开始执行这里
我这里是外围,先出去了,里面的等需要的时候执行
这里才是真正的装饰开始!
装饰结束

 

其实我前面多层装饰卡住就是这里。

装饰器的本质就是一个函数,当你写好装饰器的函数从上而下运行时,一旦遇到@的提醒,python应该就开始进入准备状态寻找需要装饰的函数,当遇到下面一个需要被装饰的函数demo时,

整个过程我在debug过程中发现,其实python马上跳转到开始去执行了warp函数,这就是刚开始我们输出前面两句的原因,其实这个时候,程序根本没有执行到demo(1,2)的地方。

在这个warp 程序的运行过程中,还发生一个命名转换的过程,就是demo=warp(),其实就是demo等于warp执行返回的函数inner,所以最后面执行demo(1,2)的时候,装饰器开始工作。

理解了这个,后面多层的装饰器更加好理解了。

 

下面上一个最简单的两层装饰器。

def deco1(func):
    print('func 1 in')
    def wrapper1():
        print('wrap1 in')
        func()
        print('wrap1 out')
    print('func 1 out')
    return wrapper1

def deco2(func):
    print('func 2 in')
    def wrapper2():
        print('wrap2 in')
        func()
        print('wrap2 out')
    print('func 2 out')
    return wrapper2

@deco1
@deco2
def foo():
    print('foo')


if __name__ == '__main__':
    foo()

 执行结果为:

func 2 in
func 2 out
func 1 in
func 1 out
wrap1 in
wrap2 in
foo
wrap2 out
wrap1 out

其实一开我因为没能理解装饰器的原理在两个地方卡住了,第一个就是为什么先出的是

func 2 in
func 2 out
func 1 in
func 1 out

第二个问题就是为什么foo函数执行了一次,第二问题,网上查到了资料,第一个问题,通过对装饰的理解和debug的过程,也理解了。

通过介绍,整个程序是至上而下运行的,在运行过程到

@deco1

@deco2

def foo():

 一遇到需要装饰的函数def的时候,用要开始重新走装饰器,在走的过程中,先走跟函数近的装饰器deco2,所以输出了
func 2 in
func 2 out
再走deco1,所以输出了
func 1 in
func 1 out

两个装饰器函数执行后,其实也运行了变量名替换。foo=deco1()即deco1程序执行的返回值wapper1,重点是来了wappper1里面的func其实已经成为deco2()即deco2执行程序的返回值wapper2,wapper2里面的func才是真正需要被装饰的函数foo。
所以在warp1 in后面执行func的时候,直接跳到warp2了。
所有在整个多层装饰的执行中,装饰器的执行是至上而下的,但在装饰器的(可以说内部调试中)是从哪个靠近需要被装饰的函数,哪个先执行,简单来说就是至下而上的。
通过这样的理解,其实无论多少层装饰器,真正被装饰的原始函数将在最下面的那个装饰器执行,另外的只不过在一层接着一层的调用函数,其实也可以认为在一次次运行装饰过程。
整个过程有点像压栈跟弹栈
最后一个带参数的装饰器。
这个更加像一个内部返回来两次函数的定义函数,一共需要三个def来写这个函数。由于日常使用不过,我的理解可能也不是很充分。

 

def deco(params):     # params 为需要传入的参数
    print('floor1')
    def inner(func):
        print('floor2')
        def warp(*args, **kwargs):
            print('floor3')
            print('装饰开始')
            for i in range(params):
                func(*args, **kwargs)
            print('装饰结束')
            print('out3')
        print('out2')
        return warp
    print('out1')
    return inner


@deco(5)        #这个就是生成一个函数warp指向demo

def demo():
    print('ok')

if __name__ == '__main__':
    demo()

 执行结果:

/Users/shijianzhong/Desktop/swiper/.venv/bin/python /Users/shijianzhong/Desktop/swiper/paramsfunc.py
floor1
out1
floor2
out2
floor3
装饰开始
ok
ok
ok
ok
ok
装饰结束
out3

Process finished with exit code 0

根据前面分析的,我废话少了点,其实在调试阶段,由于是带参数的装饰器,所以在这个三层def的情况,装饰器前期调试就已经运行了两层,且命名demo指向warp函数。

正常情况下,前面的两种情况理解,这个带参数的应该理解不难。

按照我的理解,可以先认为,在调试的过程中,装饰器自己先运行了一遍把函数返回了回来,然后用重命名了一下,这个带参数的,可以认为是装饰器自己运行了两遍,其中一遍是把参数写入内存中,待最里面的也就是真正需要装饰的函数使用。

 

今天就写这些。装饰器这下应该问题不大了,有空把闭包函数也写一下。

按照我的理解,可以先认为,在调试的过程中,装饰器自己先运行了一遍把函数返回了回来,然后用重命名了一下,这个带参数的,可以认为是装饰器自己运行了两遍,其中一遍是把参数写入内存中,待最里面的也就是真正需要装饰的函数使用。
[Ànzhào wǒ de lǐjiě, kěyǐ xiān rènwéi, zài tiáoshì de guòchéng zhōng, zhuāngshì qì zìjǐ xiān yùnxíngle yībiàn bǎ hánshù fǎnhuíle huílái, ránhòu yòng zhòng mìngmíngle yīxià, zhège dài cān shǔ de, kěyǐ rènwéi shì zhuāngshì qì zìjǐ yùnxíngle liǎng biàn, qízhōng yībiàn shì bǎ cānshù xiě rù nèicún zhōng, dài zuì lǐmiàn de yě jiùshì zhēnzhèng xūyào zhuāngshì de hánshù shǐyòng.]
As I understand it, you can believe that, in the debugging process, decorator their first run again the function returns back, and then rename the moment, the arguments can be considered decorator own run twice where the parameter is again written to memory, the innermost of which is to be true to be decorated function uses.


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM