• 是时候写一下Python装饰器了。


    装饰器记录好,就剩下多进程,多线程,携程了,前面还欠着re,不知道什么时候写,re实在太强大了。

    装饰器,记得第一次看到啥都不懂,也不知道为什么要设计这玩意,后面接触了Flask,Django才知道那些所谓的中间件都是一些装饰器。

    首先,Python是万物皆对象,所以一个函数作为对象实在太正常,但刚接触Python的时候,我至少很长时间不能接受这种行为。

    首先上一个最简单的装饰器:

    # -*- coding:utf-8 -*-
    import random
    
    def check(func):
        print('I am func,I will start first')
        def wrap(*args, **kwargs):
            res = func(*args, **kwargs)
            if res > 0:
                return res
            else:
                return 0
        print('I go out')
        return wrap
    
    @check
    def foo(m, n ):
        return random.randint(m, n)
    

     这是一个极其普通的装饰器,就是负数输出为0。

    @check所谓的语法糖很有意思,你不执行check或者foo的任意一个函数,单还是会输出
    I am func,I will start first
    I go out
    
    这个说明check函数已经执行了,check函数返回的是另一个需要被装饰的函数wrap。这里面到底发生了什么。

    当用了@check的语法糖以后,其实在执行该脚本时,Python内部先执行了foo = check(foo)
    所以,check函数会执行,而且foo也变成了check函数执行返回的wrap.
    print(foo.__name__)
    
    
    
    
    wrap
    

     在前面代码中,如果打印foo的名字代码,就会发现foo的名字都已经变了。

    现在我给foo与wrap都加上一些docstring的说明。

    import random
    
    def check(func):
        print('I am func,I will start first')
        def wrap(*args, **kwargs):
            '''My name is wrap,hello'''
            res = func(*args, **kwargs)
            if res > 0:
                return res
            else:
                return 0
        print('I go out')
        return wrap
    
    @check
    def foo(m, n ):
        '''My name is foo,hello'''
        return random.randint(m, n)
    
    print('foo_docstring---->',foo.__doc__)
    
    foo_docstring----> My name is wrap,hello
    

     从执行可以看出foo的docstring都已经发生了改变,这样对原函数实在太不友好了,不是说装饰器不影响原函数的执行,单原函数的一些内部属性发送了变化,装饰器你也太霸道了。

    Python想到了这些,有一个原装的模块可以解决。

    import random
    import functools
    
    def check(func):
        print('I am func,I will start first')
        @functools.wraps(func)
        def wrap(*args, **kwargs):
            '''My name is wrap,hello'''
            res = func(*args, **kwargs)
            if res > 0:
                return res
            else:
                return 0
        print('I go out')
        return wrap
    
    @check
    def foo(m, n ):
        '''My name is foo,hello'''
        return random.randint(m, n)
    
    print('foo_docstring---->',foo.__doc__)
    print(foo(2, 6))
    print('foo_name---->',foo.__name__)
    
    foo_docstring----> My name is foo,hello
    3
    foo_name----> foo
    

     从输出可以看出foo的内部属性在装饰器装饰后,没有发生任何改变。

    接下来,我说一下多层装饰器,理解了这个多那些框架的中间件应该有更好的理解,至少我是这么认为的。
    多层装饰器就是有多个@,这个多层装饰器,其实有那么一点点递归的感觉。

    def deco1(func):
        print("This is deco1")
    
        def wrap1(*args, **kwargs):
            print('enter deco1:')
            result = func(*args, **kwargs)
            print('exit deco1')
            return result
    
        return wrap1
    
    
    def deco2(func):
        print("This is deco2")
    
        def wrap2(*args, **kwargs):
            print('enter deco2')
            result = func(*args, **kwargs)
            print('exit deco2')
            return result
    
        return wrap2
    
    
    def deco3(func):
        print("This is deco3")
    
        def wrap3(*args, **kwargs):
            print('enter deco3')
            result = func(*args, **kwargs)
            print('exit deco3')
            return result
    
        return wrap3
    
    
    @deco1
    @deco2
    @deco3
    def foo2(x, y):
        return x ** y
    
    print('_' * 100)
    
    foo2(3, 4)
    
    
    
    
    This is deco3
    This is deco2
    This is deco1
    ____________________________________________________________________________________________________
    enter deco1:
    enter deco2
    enter deco3
    exit deco3
    exit deco2
    exit deco1
    

     上面的代码做的是一个三层的装饰的,首先Python还是根据语法糖,分别执行了三个装饰器,所以先输出了三个装饰函数里面的打印输出。

    从初始化来看,装饰器初始化是从下到上的。再来分析一下,装饰器在执行装饰器函数的时候,做了什么赋值。

     下面是啰嗦的解释,不想看啰嗦的解释,应该foo2 = deco1(deco2(deco3(foo2)))

    初始化过程中,首先foo2被装饰

    foo2 = deco3(foo2)的返回函数wrap3

    接下来:wrap3的函数继续传递给deco2

    foo2 = deco2(wrap3)返回函数wrap2

    再接下来:wrap2的函数继续传递给deco1

    foo2 = deco1(wrap2)返回函数wrap1

    所以初始化的最后,foo2变成了wrap1

    所以在执行foo2()的过程,就变成了先执行wrap1,执行wrap1就要执行wrap1里面的func为wrap2,执行wrap2就是执行wrap2里面的wrap3,最后wrap3执行它里面的foo2函数。

    但foo2函数执行后,从wrap3开始执行返回,通过result传递,最后传递到wrap1的result,为了执行foo,其实装饰器做了很多,感觉很绕。

    很多框架的多层装饰器也差不多,为了判断你的函数能否执行,前面加了很多装饰器来帮助过滤参数,当你执行函数以后,也可以通过向外传递的过程中,设置一些条件,来限制最终的函数执行结果能不能传递出去。其实这也是我为什么现在不急着上手Django,Flask的原因,希望学好了基础,再去学那些框架,不仅知道如何用,更加知道为什么这么用,这样我觉的学起来会轻松很多。

    再来一个带参数的装饰器,带参数的装饰器本质就是一个装饰器的生产机器,就是再装饰器的外面又套了一层函数,只不过返回了是这个装饰器。

    def sum_rand(n):
        def lll(func):
            def wrap(*args, **kwargs):
                c = 0
                for i in range(n):
                    c += func(*args, **kwargs)
                return c
            return wrap
        return lll
    
    
    
    
    @sum_rand(90000)           # 执行了生成一个lll的装饰器
    def fxx(x, y):
        return x * y
    
    print(fxx(2, 3))
    

     上面是一个带参数的装饰器,返回的是一个两数乘积累加多次的和。

    @sum_rand(90000)就是一个装饰器的生产器
    其实@sum_rand(90000)就是lll装饰器函数,只不过为什么需要读取他的参数,套在装饰器的外层,然后让它返回一个装饰器。


    写到这里函数的装饰器差不多就这些,回头来看,其实也就那么一回事,下一个章节,我会记录一下类的装饰器。
    
    
     
  • 相关阅读:
    IDEA开发常用快捷键
    常见操作系统的分类
    预科班第四次考核总结
    博客园使用office 365写博客
    大三上学期十二周总结
    python循环和布尔表达式总结
    灵活运用绑定变量---declare匿名块使用绑定变量
    Oracle 10G强大的SQL优化工具:SQL Tuning Advisor
    40个DBA日常维护的SQL脚本--1113
    awr相关指标解析
  • 原文地址:https://www.cnblogs.com/sidianok/p/11943705.html
Copyright © 2020-2023  润新知