• Python学习日记(七)——装饰器


    1.必备知识

    #### 一 ####
    def foo():
        print 'foo'
     
    foo     #表示是函数
    foo()   #表示执行foo函数
     
    
    #### 二 ####
    def foo():
        print 'foo'
     
    foo = lambda x: x + 1
     
    foo()   
    # 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了
    

    为什么需要装饰器?

    来自知乎大神的形象比方

    内裤可以用来遮羞,但是到了冬天它没法为我们房等御寒,宗明的人发明了长裤,有了长裤后宝宝再也不冷了,装饰器在这里就像我们说的长裤,在不影响内裤作用下,给我们的身子提供了保暖的功效。

    回到主题

    装饰器本事是一个Python函数,它可以让其他函数在不需要任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续使用。概括来讲装饰器的作用就是为已经存在的对象添加额外的功能。  

    先看一个简单的例子

    def func():
        print('i am ciri')
    

    现有一个新需求,希望可以在 输出的内容 之前和之后加些东西:

    def f1():
        print('ciri is my friend')
        print("i am ciri")
        print('hi ciri')

    f2()和f3()也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成了大量雷同的到吗为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志,日志处理完成之后,在执行真正的代码。

    def outer(func):
        print("ciri is my friend")
        func()
        print("ciri is my friend")
    
    def f1():
        print('i am ciri')
    
    
    use_logging(f1)

    逻辑上不难理解,但是折让的话,我们每次都要将一个函数作为参数转递给use_logging函数。而且这种方式破坏了原有的代码逻辑结构,之前执行业务逻辑是,执行f1(),但是现在不得不修改成use_logging(bar)。那么有没有更好的方式呢?当然是有,答案就是装饰器。

    def outer(func):
        def inner():
            print("ciri is my friend")      
            r = func()
            print('hi ciri')      
            return r            
        return inner()
    
    @outer
    def f1():   
        print("i am ciri")

    装饰器原理

    在python中@符号具有特殊性,在python中遇到@会执行两步

    • 第一步:执行outer函数,并且将其下面的函数名当做参数——(f1 就是 func)
    • 第二步:将outer的返回值重新赋值给 函数 f1 —— f1 等于outer的返回值
    def outer(func):    #第一步,f1作为outer函数的参数,此时func就是就f1函数
        def inner():
            print("ciri is my friend")
            r = func()      #对应第一步,func就是 f1 的原函数
            print("hi ciri")
            return r
    
        return inner
        #第二步:将outer的返回值重新赋值给f1,所以 f1 等于 outer的返回值
        #返回值是inner这个函数,不是执行这个函数,执行括号需要加括号inner——()
        #此时f1变成了新函数——inner
    
    
    @outer      #第一步: f1就当做outer函数的参数,f1 = func,因为f1是个函数,所以func代指f1原来的函数
    def f1():
        print("i am ciri")
    
    f1()
    

    用中文描述装饰器的执行过程:

    def outer(func): 
        def inner():
            print("ciri is my friend")
            r = func()      
            print("hi ciri")
            return r
        return inner
    
    @outer     
    def f1():
        print("i am ciri")
    
    f1()
    
    """
    解释器从上往下执行,把outer函数放到内存
    然后遇到@,执行两步
        第一步:执行outer函数,并且将其下面的函数名当做参数
            此时参数func就指向了——旧f1函数    
        第二步:将outer的返回值重新赋值给f1
            此时outer的返回值是inner函数,所以f1在内存中指向inner函数,所以f1变成了——新函数inner
    放到内存中
    
    遇到f1(),执行函数f1因为f1现在已经指向了函数inner
    所以会输出
        ciri is running
    然后遇到func(),因为func指向旧f1函数,所以执行原f1函数的内容,输出
        i am ciri
        并把返回值赋值给r,r就是原函数的返回值
    然后输出
        hi ciri
    返回原函数的返回值r,遇到return函数执行结束
    """
    

    问答时间 

    问题:被修饰的函数如果有参数呢?

    #带一个参数的装饰器
    
    def outer(func):
        def inner(a1):
            print("cute")
            func(a1)
            print("charming")
        return inner
    
    @outer
    def index(a1):
        print("ciri")
        return a1
    
    index(1)
    
    #因为index变成了func的内存函数inner,所以假如index有参数,inner也需要有参数
    #又因为func保存的是原index函数,所以也需要有参数
    一个参数
    #带两个参数的装饰器
    
    def outer(func):
        def inner(a1,a2):
            print("cute")
            func(a1,a2)
            print("charming")
        return inner
    
    @outer
    def index(a1,a2):
        print("ciri")
        return a1 + a2
    
    index(1,2)
    两个参数

    问题:可以装饰具有n个参数的函数的装饰器吗?

    #万能参数
    def f1(*args,**kwargs):
        print(args)
        print(kwargs)
    
    f1(1,2,3,44,k1=123,k2=423)
    
    #注必须按顺序来,不能写成f1(1,2,2,3,k1=123,423)
    #也不能写成f1(k1=123,k2=423,1,2,3)
    注:万能参数
    #带N个参数的装饰器
    
    def outer(func):
        def inner(*args,**kwargs):
            print("cute")
            ret = func(*args,**kwargs)
            print("charming")
            print(ret)
        return inner
    
    @outer
    def index(a1,a2):
        print("ciri")
        return a1 + a2
    
    @outer
    def home(a1):
        print("home")
        return "home"
    @outer
    def show(a1,a2,a3):
        print("show")
        return "show"
    
    index(1,2)
    #index的1,2会顺利的封装到inner函数的args里面去,
    #但是index的参数是(a1,a2),就需要做一堆判断去把参数准确的传进去
    #如果args里面有值,就把第一个值放到a1里面去,把第二个值放到a2里面去,值越多越麻烦
    #python在这方面进行了优化,func函数也写成这样(*args,**kwargs)就会自动进行做操作
    home(1)
    show(1,2,3)

    问题:一个函数可以被多个装饰器修饰吗?

    #多个装饰器装饰同一个函数
    def outer_1(func):
        def inner(*args,**kwargs):
            print("i love you")
            ret = func(*args,**kwargs)
            return ret
        return inner
    
    def outer(func):
        def inner(*args,**kwargs):
            print("cute")
            ret = func(*args,**kwargs)
            print("charming")
            return ret
        return inner
    
    @outer_1
    @outer
    def index(a1,a2):
        print("ciri")
        return a1 + a2
    
    index(1,2)
    

    注:一个函数被多个装饰器修饰的原理

      

      

     

  • 相关阅读:
    别再为了this发愁了------JS中的this机制
    offsetLeft,Left,clientLeft的区别
    下拉菜单的实现classList.add() classList.remove() class属性的添加和删除
    for循环
    html的基本数据类型(数字,字符串, 列表, 字典)
    定时器setInterval, innerText获取文本, charAt()获取单个字符串, substring(1, content.length)获取范围内的字符串, 实现字符串的滚动效果
    定时器 setInterval(‘function()’, 2000)
    parseInt 和 parseFloat 实现字符串转换为数字
    javarscript在HTML中的调用方式 (直接调用 和文件调用)
    input文本框 放上图片img 通过padding relative和absolute 的实现
  • 原文地址:https://www.cnblogs.com/houzhaohui/p/7393969.html
Copyright © 2020-2023  润新知