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函数时的是”自上而下“

posted on 2019-09-02 16:38  海龟先生  阅读(266)  评论(0编辑  收藏  举报

导航