zoukankan      html  css  js  c++  java
  • Python学习笔记-Day60-装饰器

    今日内容:

    一、函数名的应用

    二、闭包

    三 、装饰器

    四、装饰器进阶

    一、函数名的应用

    1、函数名可以当作变量赋值给另一个变量

    2、函数名可以作为参数

    3、函数名可以作为返回值

    4、函数名可以作为容器中的元素

    二、闭包

    1、闭包的灵魂三问

    ① 

    def func1():
        name = '张三'
        
        def func2(arg):
            print(arg)
        func2(name)
    
    func1()

    ② 

    def func1():
        name = '张三'
        
        def func2():
            print(name)  # 能够访问外层作用域的变量
        func2()
    func1()

    ③ 

    def func1(name):  # 函数的形参相当于在函数内部定义了一个变量
        
        def func2():
            print(name)   # 能够访问到外层作用域的变量
        func2()
        
    func1('张三')

    2、什么是闭包?

    闭包就是内层函数调用外层函数的(非全局的)变量,这个内层函数就是一个闭包;在Python中,可以用__closure__来检测一个函数是否是闭包

    3、闭包的作用

    如果Python中的一个函数,引用了外层函数的变量,那么这个变量不会随着外层函数的结束而被销毁,而是会被保留在内存中。

    也就是说,闭包可以保留变量的使用

    4、如何在函数的外部调用内层函数?

    将内层函数名作为外层函数的返回值

    def wrapper():
        name='张三'
       
       def inner(): print(name) return inner#把内部函数当作返回值返回
    ret=wrapper()#把返回值赋值给变量ret,ret=inner ret()#就可以调用内部函数,相当于inner()

    三、装饰器的定义

    1、装饰器的来历

    软件设计原则:开闭原则(开放封闭原则)

    对扩展代码的功能是开放的,但是对修改源代码是封闭的,这样的软件设计能更好的开发和维护我们的代码

    2、装饰器的作用

    在不改变函数的结构和调用方式的基础上,动态的给函数添加功能

    例如:我们自己写的给不改变函数的调用方式,给create_people添加功能

    def create_people():  # 原来的函数
        print('女娲造人啦!!')
    
    def a(func):  # 加了功能的函数
        def b():
            print('加水')
            func()
        return b
    
    create_people = a(create_people)   # 给create_people重新赋值,相当于create_people = b
    create_people()  # 相当于执行了b函数
    # 最后的结果是:
    # 加水
    # 女娲造人啦!!

    3、装饰器语法糖

    利用装饰器,可以简化上面的代码

    def wrapper(func):   # 装饰器
        def inner():
            print('加水')
            func()
        return inner
    
    @wrapper
    def create_people():  # 原来的函数
        print('女娲造人啦!!')
    
    create_people()  # 装饰之后的函数
    # 最终的结果还是:
    # 加水
    # 女娲造人啦!!

    @wrapper的作用是:

      ① 将create_people作为参数传给wrapper函数

      ② 将inner函数赋值给create_people函数

    四、装饰器的进阶

    1、装饰带返回值的函数

    就是在装饰器的inner函数中获取原函数的返回值,并在inner函数最后将其作为返回值返回

    def wrapper(func):
        def inner():
            print('这是新功能')
            r = func()   # 获取func原来的返回值
            return r  # 返回func原来的返回值
        return inner
    
    @wrapper
    def f1():
        return '嘿嘿嘿'
    
    ret = f1()  # 执行了新功能
    print(ret)  # 返回了原来的f1函数的返回值"嘿嘿嘿"

    2、装饰带参数的函数

    让装饰器函数中的inner函数带上和被装饰函数一样的参数,通用的写法是inner(*args,**kwargs)

    def wrapper(func):
        def inner(x,y):
            print('这是新功能')
            func(x,y)
        return inner
    
    @wrapper()
    def f1(x,y):
        print("{}+{}={}".format(x,y,x+y))
    #
    f1(10,20)  # 这是新功能 10+20=30

    3、带参数的装饰器

    有时候,我们需要为我们的装饰器传递参数

    我们使用装饰器是直接@wrapper,没有可以加参数的地方;

    此时我们可以在原来的装饰器的外层再定义一个函数用来传参,并在最后返回原来的装饰器函数名。

    def d(a=None):  # d是新添加的最外层函数,为我们原来的装饰器传递参数,a就是要传递的参数
        def wrapper(func):  # wrapper 就是我们原来的装饰器函数,func是被装饰的函数
            def inner(*args,**kwargs):  #args和kwargs是被装饰函数的参数
                if a:
                    print('欢迎来到{}页面'.format(a))
                else:
                    print('欢迎来到首页')
                func(*args,**kwargs)  # 调用被装饰的函数,接收参数
            return inner
        return wrapper
    
    @d()  # 不给装饰器传参数,使用默认的None参数
    def index(name):
        print('hello{}'.format(name))
    
    @d('电影')  # 给装饰器函数传参数
    def movie(name):
        print('hello{}'.format(name))
    
    if __name__ == '__main__':
        index('张三')  # 欢迎来到首页 hello张三
        movie('张三')  # 欢迎来到电影页面 hello张三

    4、多个装饰器装饰同一个函数

    同一个函数可以被多个装饰器装饰,此时需要注意装饰器的装饰顺序和函数的执行顺序

    def wrapper1(func):
        print('d1')
        def inner1():
            print('inner1')
            return '<i>{}</i>'.format(func())
        return inner1
    
    def wrapper2(func):
        print('d2')
        def inner2():
            print('inner2')
            return '<b>{}</b>'.format(func())
        return inner2
    
    @wrapper1
    @wrapper2
    def f1():
        return "Hello Andy"
    
    ret = f1()  # 结果是 inner1 inner2
    print(ret)  # 结果是 <i><b>Hello Andy</b></i>

    装饰顺序:

    ① f1先被wrapper2装饰,所以先打印”d2“,此时 f1 = inner2

    ② 然后被wrapper2装饰,打印”d1“,此时 f1 = wrapper1(inner2) = inner1

    函数执行的时候:

    ① 函数刚开始执行的时候,f1 = inner1,所以先执行inner1函数,所以先打印inner1,此时返回值是<i>{}</i>.format(inner2())

    ② 执行inner2函数,打印inner返回值2,此时返回值是<b>{}</b>.format(f1())

    ③ 执行f1函数,返回值是”Hello Andy“,再逐一将结果返回,最终的返回值是<i><b>Hello Andy</b></i>

  • 相关阅读:
    刷题总结——射箭(bzoj2732)
    算法复习——半平面交(bzoj2618凸多边形)
    刷题总结——spoj1812(后缀自动机+DP)
    刷题总结:最长公共字串(spoj1811)(后缀自动机)
    算法复习——后缀自动机
    算法复习——splay+启发式合并(bzoj2733-永无乡)
    北京集训TEST13——PA(Goodness)
    linux命令学习笔记(46):vmstat命令
    linux命令学习笔记(45):free 命令
    linux命令学习笔记(44):top命令
  • 原文地址:https://www.cnblogs.com/tian-tian/p/9886392.html
Copyright © 2011-2022 走看看