zoukankan      html  css  js  c++  java
  • python之函数装饰器


    装饰器(闭包函数的一种应用)

    那么,我们就要先理解,什么是闭包函数。

    首先,让我们先看两段代码:

    def foo():
        num = 10 # 局部变量
    print(num)  # NameError: name 'num' is not defined
    # 对于print而言,是不知道num这个变量的存在的。
    num = 10 # 全局变量
    def foo():
        print(num)  # 10
    # 对于print而言,可以找到这个num变量。

    (以上两个例子会涉及到变量的作用域的问题,回头再说)

      下面再来看一段代码:

    def print_msg():
        # print_msg 是外围函数
        msg = "it is a msg"
    
        def printer():
            # printer是嵌套函数
            print(msg)
        printer()
    # 输出 it is a msg
    print_msg()
    

      仔细观察上面的代码,你会发现,printer这个方法,是藏在print_msg这个方法的内部的,也就是说,对于使用print_msg的人来说,是不知道printer这个方法的存在的。

    但是,有没有一种做法,能够让外面使用的人,知道内部的东西呢?

    这时候,闭包函数,就应运而生了。也就是说,闭包函数就是为了是局部变量在函数外被访问成为可能。再来看一段代码:

    def print_msg():
        # print_msg 是外围函数
        msg = "it is a msg"
        def printer():
            # printer 是嵌套函数
            print(msg)   # 内嵌函数引用了外围函数的变量
        return printer
    
    another = print_msg()   # 调用这个print_msg方法,返回的是printer这个函数的内存地址。这是重点。
    # 输出 it is a msg
    another()
    

      仔细观察上面的代码,你会发现,其实another这个变量,指向的就是printer函数的内存地址。那么,another()不就是printer()吗?你会发现,通过return这个操作,我们就将print_msg方法内部的东西,拿出来了。这就是闭包的意义所在。

    ok,理解了啥是闭包,我们来看看啥事装饰器。

    再来看一段代码:

    def print_msg(out_msg):
        # print_msg 是外围函数
        msg = out_msg
    
        def printer():
            # printer 是嵌套函数
            print(msg)  # 内嵌函数引用了外围函数的变量
    
        return printer
    
    
    another = print_msg(out_msg="qwer")  # 调用这个print_msg方法,返回的是printer这个函数的内存地址。这是重点。
    # 输出 qwer
    another()   
    

      


    装饰器(闭包函数的一种应用)

    1. 什么是装饰器
    首先,女人为啥要化妆(装饰自己)?为了好看,是吧。那么,程序为啥要化妆(装饰自己)呢?那是为了功能更丰满。所以,装饰器就是用来为被装饰器对象添加新功能的工具。

    需要注意的是:装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象


    2. 为何要用装饰器
    因为在实际开发环境中,是不允许对原代码进行操作,而且不能破坏原有的调用关系,根据实际要求还需要增添新功能
    开发需要遵循的一个原则
    开放封闭原则:封闭指的是对修改封闭,对扩展开放,就是保持原有功能并新增新的功能

    装饰器的实现必须遵循两大原则:
    1. 不修改被装饰对象的源代码,被操作的函数的源代码是不能进行修改
    2. 不修改被装饰器对象的调用方式

    装饰器的目标:就是在遵循1和2原则的前提下为被装饰对象添加上新功能

    3. 怎么用装饰器

    无参装饰器
    装饰器不需要参数传入
    有参装饰器
    装饰器需要参数传入

    # 装饰器模板
    def wrapper(func):
        def inner(*args, **kwargs):
            # 在调用函数前加功能
            res = func(*args, **kwargs)  # 调用被装饰的,也就是最原始的那个函数
            # 在调用函数后加功能
            return res
    
        return inner
    

      

      我们来分析一下上面的例子:

        首先,有个外部函数,有一个变量,这个函数返回了内部函数的一个内存地址。那么

    如果外部这么使用:func = wrapper(func)

    那就相当于:func = inner # 因为wrapper的返回值是inner这个函数的内存地址。

    那么func()就相当于inner()。仔细看着inner这个函数,其实内部还是执行的func这个函数(是作为wrapper的变量传进去的),

    也就是说,最后的最后,其实转了一大圈,又回来了。

    那么,为啥要转一大圈呢,不是闲着没事干。

    而是为了在真正执行函数之前,加点啥骚操作,

    在执行函数之后,加点骚操作。

    但是对于运行函数的人而言。并没有察觉到我们加了骚操作。

    语法糖
    什么是语法糖?
    使用了”@”语法糖后,就不需要额外代码来给”被装饰函数”重新赋值了,
    其实”@装饰器函数”的本质就是”被装饰函数 = 装饰器函数(被装饰函数#原始的被装饰函数的内存地址)”。

    为什么用语法糖?
    使用语法糖让代码简化,不需要重复在调用装饰器时,进行重复的赋值操作。

     

    也就是说:

    @wrapper
    def test_print():
        print("这是打印函数")
    其实就是:
    test_print = wrapper(test_print) <==> @wrapper

    那么,上么的代码也可以写为:

     

    def test_print():
        print("这是打印函数")
    
    
    test_print = outter(test_print)
    test_print()

    例子:

    # 装饰器模板
    def outter(func):
        def wrapper(*args, **kwargs):
            # 在调用函数前加功能
            print('before')
            res = func(*args, **kwargs)  # 调用被装饰的也就是最原始的那个函数
            # 在调用函数后加功能
            print('after')
            return res
    
        return wrapper
    
    
    @outter
    def test_print():
        print("这是打印函数")
    
    
    # test_print = outter(test_print)
    test_print()
    

      

    ===========

    后话:

    注意:
    1.需要注意的是装饰器最多有三层
    2.解释装饰器时@语法的时候是”自下而上运行“
    3.执行装饰器时装饰器内的那个wrapper函数时的是”自上而下“

  • 相关阅读:
    awk
    Python自动化开发之python的常用模块
    sed
    python自动化开发-8
    正则表达式-2-正则表达式实战1
    linux之sort和uniq
    SSH免密登录
    Docker下安装Mongodb
    Docker下安装Elasticsearch
    Docker下安装RabbitMQ
  • 原文地址:https://www.cnblogs.com/haiguixiansheng/p/11447375.html
Copyright © 2011-2022 走看看