zoukankan      html  css  js  c++  java
  • python高级-装饰器(19)

    一、什么是闭包

    先看一个例子:

    #定义一个函数
    def test(number):
        #在函数内部在定义一个函数,并且这个函数用到外围函数的变量
        #那么将这个函数及用到的一些变量称之为闭包
        def test_in(number_in):
            print("在test_in函数内部,number_in的值为:%d"%number_in)
            return number+number_in
            #其实这里返回的是闭包,也就是内部的函数引用
        return test_in
    #给test函数赋值,这个20就是参数number
    ret = test(20)
    #注意这里的100就是参数number_in
    print(ret(100))

    运行结果为:

    在test_in函数内部,number_in的值为:100
    120

    说明:

    • 在函数内部在定义一个函数,并且这个函数用到外围函数的变量,那么将这个函数及用到的一些变量称之为闭包
    • 在其他语言里面不允许函数内部在定义函数,但是python中的闭包可以

    二、什么是装饰器

    装饰器是程序开发中经常会⽤到的⼀个功能,所以这也是Python⾯试中必问的问题。

    定义:

    1. 装饰器本身就是一个函数
    2. 为其他函数提供附加功能
    3. 不改变被修饰函数的源代码
    4. 不改变原调用方式
    5. 装饰器=高阶函数+嵌套函数

    知识点:

    • 函数本身就是一个变量(意味着可以被复制给一个变量:test=test(1) )
    • 高阶函数:把函数名当成一个实参传递给另一个函数func(test1) (不改变源代码的前提下添加代码)
    • 返回值中包含函数名return deco (不改变函数的调用方式)
    • 嵌套函数:函数中加入新的函数

    典型结构:

    def func(args):
        def func_in(args_in):
             pass
         return func_in

    三、装饰器案例

    1、先看一个例子

    某公司有多个研发部⻔,1个基础平台部⻔,基础平台负责提供底层的功能,如:数据库操作、redis调⽤、监控API等功能。研发部⻔使⽤基础功能时,只需调⽤基础平台提供的功能即可。如下:

    --------------基础平台提供的功能--------------
    def func1():
        pass
    def func2():
        pass
    def func3():
        pass
    
    --------------研发部门A使用基础平台--------------
    func1()
    func2()
    func3()
    
    --------------研发部门B使用基础平台--------------
    func1()
    func2()
    func3()

    随着项目进度的深入,产品经理提出,要在基础平台的提供的所有功能中,添加验证功能,不能谁都可以使用基础平台的全部功能,即执行功能前,先进行验证。

    项目经理将此功能交给了小A去实现。

    小A就去和每个研发部沟通,让每个研发部自己把验证的代码加上,结果第二天就被辞职了。

    项目经理又将此功能交给了小B去实现。

    小B吸取小A的经验,开始自己改代码:

    --------------基础平台提供的功能--------------
    def func1():
        #验证1
        #验证2
        pass
    def func2():
        #验证1
        #验证2
        pass
    def func3():
        #验证1
        #验证2
        pass
    
    --------------研发部门A使用基础平台--------------
    func1()
    func2()
    func3()
    
    --------------研发部门B使用基础平台--------------
    func1()
    func2()
    func3()

    没过多久小B也被开除了。。。

    项目经理又把工作交给了小C,小C对基础平台代码进行重构,其他业务部门无需做任何修改

    --------------基础平台提供的功能--------------
    def check_login():
        #验证1
        #验证2
        pass
    
    def func1():
        check_login()
        pass
    def func2():
        check_login()
        pass
    def func3():
        check_login()
        pass
    
    --------------研发部门A使用基础平台--------------
    func1()
    func2()
    func3()
    
    --------------研发部门B使用基础平台--------------
    func1()
    func2()
    func3()

    项目经理看后表示还不错,但是感觉还是差了一点点,于是决定不再低调,再也不让小弟做了,于是自己做了一个方案:

    --------------基础平台提供的功能--------------
    def check_login(func):
        def inner():
            #验证1
            #验证2
            func()
        return inner
    
    @check_login
    def func1():
        pass
    
    @check_login
    def func2():
        pass
    
    @check_login
    def func3():
        pass
    
    --------------研发部门A使用基础平台--------------
    func1()
    func2()
    func3()
    
    --------------研发部门B使用基础平台--------------
    func1()
    func2()
    func3()

    对于上述代码,也是仅仅对基础平台的代码进⾏修改,就可以实现在其他⼈调⽤函数 func1(), func2(), func3()之前都进⾏【验证】操作,并且其他研发部⻔⽆需做任何操作。

    单独以func1()为例讲解:

    def check_login(func):
        def inner():
            #验证1
            #验证2
            func()
        return inner
    
    @check_login
    def func1():
        pass

    python解释器就会从上到下解释代码,步骤如下:

    1 def check_login(func): ==>将check_login函数加载到内存
    2 @check_login

    没错, 从表⾯上看解释器仅仅会解释这两句代码,因为函数在没有被调⽤之前其内部代码不会被执⾏。从表⾯上看解释器着实会执⾏这两句,但是 @check_login这⼀句代码⾥却有⼤⽂章, @函数名 是python的⼀种语法糖

    上例@check_login内部会执⾏⼀下操作:

    执行check_login函数,并将@check_login下面的函数作为check_login函数的参数,

    即@check_login等价于check_login(func1),所以内部就会去执行:

    def check_login(func):
        def inner():
            #验证1
            #验证2
            func() #func是参数。此时的func就是函数func1()
        #返回inner,inner的内部就是执行func1()函数,但是执行func1()函数前,进行了验证1,验证2
        return inner

    check_login() 的返回值

    将执行完的chenk_login函数返回值赋值 给@check_login下面的函数的函数名func1 即将check_login()的返回值再重新赋值给func1,即:

    新func1 = def inner():
                #验证1
                #验证2
                func() #func是参数。此时的func就是函数func1()
                #返回inner,inner的内部就是执行func1()函数,但是执行func1()函数前,进行了验证1,验证2
                return inner

    所以,以后研发部门想要执行func1函数时,就会执行新func1函数,在新func1函数内部先执行验证,再执行原来的func1函数,然后将原来func1函数的返回值返回给了业务调用者。

    四、装饰器应用

    #定义一个装饰器:实现加粗效果
    def makeBold(fn):
        def wrapped():
            return "<b>"+fn()+"</b>"
        return wrapped
    
    #定义一个装饰器:实现斜体效果
    def makeItalic(fn):
        def wrapped():
            return "<i>"+fn()+"</i>"
        return wrapped
    
    #使用装饰器装饰函数
    @makeBold
    def test():
        return "Hello World"
    
    #使用装饰器装饰函数
    @makeItalic
    def test1():
        return "Hello World"
        
    @makeBold
    @makeItalic
    def test2():
        return "Hello World"
    
    print(test())
    print(test1())
    print(test2())

    运行结果为:

    <b>Hello World</b>
    <i>Hello World</i>
    <b><i>Hello World</i></b>

    五. 装饰器示例

    例1:⽆参数的函数

    def test_out(func):
        def test_in():
            print("name-%s"%func.__name__)
            func()
        return test_in
    
    @test_out
    def test():
        pass
    
    test()

    运行结果为:name-test

    例2:被装饰的函数有参数

    def test_out(func):
        def test_in(a,b):
            print(a,b)
            func(a,b)
        return test_in
    
    @test_out
    def test(a,b):
        print("a+b=",a+b)
    
    test(1,2)

    运行结果为:

    1 2
    a+b= 3

    例3:被装饰的函数有不定⻓参数

    def test_out(func):
        def test_in(*args,**kwargs):
            func(*args,**kwargs)
        return test_in
    
    @test_out
    def test(*args,**kwargs):
        print(args,kwargs)
    
    test(1)
    test(1,2)
    test(1,2,3,k="v")

    运行结果为:

    (1,) {}
    (1, 2) {}
    (1, 2, 3) {'k': 'v'}

    说明:如果被修饰的函数有参数,则装饰器内部的函数也要有同样个数的参数才可以匹配成功。

    例4:装饰器中的return

    def test_out(func):
        def test_in():
            func()
        return test_in
    
    @test_out
    def test():
        return "hello"
    
    result = test()
    print(result)

    运行结果为:None

    如果修改装饰器为 return func() ,则运⾏结果:

    def test_out(func):
        def test_in():
            return func()
        return test_in
    
    @test_out
    def test():
        return "hello"
    
    result = test()
    print(result)

    运行结果为:hello

    ⼀般情况下为了让装饰器更通⽤,可以有return

    例5:装饰器带参数,在原有装饰器的基础上,设置外部变量

    六、类装饰器

    装饰器函数其实是⼀个接⼝约束,它必须接受⼀个callable对象作为参数,然后返回⼀个callable对象。在Python中⼀般callable对象都是函数,但也有例外。只要某个对象重写了 __call__() ⽅法,那么这个对象就是callable

    class Test():
        def __call__(self):
            print("call me")
    
    t = Test()
    t()

    执行结果:call me

    类装饰器demo

    class Test():
        def __init__(self,func):
            print("----初始化----")
            print("func name is %s"%func.__name__)
            self.__func = func
    
        def __call__(self):
            print("----类装饰器的功能----")
            self.__func()
            print("----类装饰器执行完毕----")
    @Test
    def test():
        print("----test----")
    
    test()

    运行结果为:

    ----初始化----
    func name is test
    ----类装饰器的功能----
    ----test----
    ----类装饰器执行完毕----

    说明:

    • 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
    • 并且会把test这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量执行了test函数体
    • test函数相当于指向了用Test创建出来的实例对象
    • 挡在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
    • 为了能够在__call__方法中调用原来的test指向的函数体,所以在__init__方法中就需要一个实例属性保存这个引用,所以才有了self.__func = func这句代码,从而在调用__call__方法中就能调用test
  • 相关阅读:
    学习笔记-Bootstrap
    学习笔记-JavaScript
    学习笔记-HTML
    学习笔记-数据库操作
    学习笔记-MATLAB
    学习笔记-mysql基本操作
    学习笔记-Java进阶
    学习笔记-Java入门
    学习笔记-考研英语
    学习笔记-线代
  • 原文地址:https://www.cnblogs.com/Se7eN-HOU/p/10724523.html
Copyright © 2011-2022 走看看