其他分享
首页 > 其他分享> > 14. 闭包函数与装饰器

14. 闭包函数与装饰器

作者:互联网

一 、 闭包函数

1. 闭包函数的两大特征

闭:定义在函数内部的函数

包:内层函数使用了外层函数名称空间中的名字

def outer():
    x = 999
    def inner():
        print('外层函数的内层函数inner', x)  # 内层函数使用到了外层函数名称空间中名字
    return inner
x = 666 res = outer() # 得到的返回值是函数名inner res() # 相当于inner() # 外层函数的内层函数inner 999

2. 闭包函数的实际应用

闭包函数是给函数体传参的另外一种方式

函数体传参的方式1:形参

def f1(name):
    print(name)  # 函数体代码需要什么就可以在形参中写什么

f1('LeoMessi')

函数体传参的方式2:闭包

# 闭包传参的方式1:外层函数的变量值固定
def outer():
    name = 'LeoMessi'
    def inner():
        print(name)  # 永远使用的都是外层函数固定的变量值LeoMessi
    return inner
res = outer() res() # 相当于inner() # LeoMessi

 

 

 二、 装饰器简介

概念:在不改变被装饰对象原有的'调用方式'和'内部代码'的情况下,给被装饰对象添加新的功能

装饰器的原则:对扩展开放,对修改封闭

 

需求:统计函数的执行时间

# 预备知识:time模块
import time
print(time.time())  # 时间戳,1970年1月1日0时0分0秒距离刚刚代码运行间隔的秒数
实现方法1:(方法1没有修改被装饰对象的调用方式,也没修改内部代码,但可扩展性差)
import time
def f1():
    time.sleep(3)
    print('f1函数已运行')
start_time = time.time()  # 函数执行之前获取一个时间戳
f1()
end_time = time.time()  # 函数执行之后获取一个时间戳
print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间
# f1函数已运行
# 3.0044925212860107

三、 简易版本装饰器

需求:如果在很多地方都需要调用f1,如何统计f1的执行时间,即在很多地方都需要执行统计f1执行时间的代码,就用到了函数

实现方法2:(函数的实现方式:将函数名通过形参传入,封装成函数之后,调用方式变了,不符合装饰器原则)
import time
def f1():
    time.sleep(3)
    print('f1函数已运行')
def get_time(func): start_time = time.time() func() end_time = time.time() print(end_time - start_time)
get_time(f1)

 

方法2给函数体直接传参无法实现装饰器,采用另一种给函数体传参的方式(闭包函数)

2 res=get_time函数指向的函数体代码
3 func=函数体f1指向的函数体代码
7 执行get_time函数体代码
9 执行f1函数体代码

 

为了解决方法3外层函数变量值固定的问题,采用向外层函数传参的方式

1和2会在全局名称空间中产生两个名字f1和outer分别指向两个函数体代码
6使用一个变量名接收outer函数的返回值,该变量名与全局函数名f1冲突了:
里面的f1函数名就被普通的变量名f1替换了(全局只有一个普通的变量名f1) 7通过f1指向的是get_time函数体代码 9执行的才是真正的f1函数

四、 进阶版本装饰器

 

 

 五、 完整版本装饰器

# 实现方法6:(观察被装饰函数的返回值)
import time
def f1(a, b):
    time.sleep(1)
    print(a, b, 'f1函数已运行')
    return '哈哈,和想象的有点不一样吧'
res = f1(11, 22)
print(res)  # 哈哈,和想象的有点不一样吧
def outer(func_name):
    def get_time(*args, **kwargs):
        start_time = time.time()
        func_name(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return get_time
f1 = outer(f1)
res = f1(11, 22)  # 接收函数的返回值
print(res)  # None(因为get_time函数没有返回值,想要在这里实现与直接调用函数相同的效果,失败)

 

 

 六、 装饰器模板

装饰器的经典模板

def f1(*args, **kwargs):
    pass

def outer(func_name):  # func_name用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        print('执行被装饰函数之前,可以做的额外操作')
        res = func_name(*args, **kwargs)  # 执行真正的被装饰函数,res的作用是接收原函数被调用后的返回值
        print('执行被装饰函数之后,可以做的额外操作')
        return res  # 返回真正函数的返回值
    return inner

f1 = outer(f1)
f1()

 

# 实战案例:校验用户信息是否正确
def f1(*args, **kwargs):
    print('这是f1函数,只有LeoMessi才可以调用')

def outer(func_name):  # func_name用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        username = input('请输入用户名:')  # 执行f1函数之前,先获取用户数据
        password = input('请输入密码:')
        if username == 'LeoMessi' and password == '30':
            res = func_name(*args, **kwargs)  # 执行真正的被装饰函数,res的作用是接收原函数被调用后的返回值
            return res
        else:
            print('Sorry, no qualification')
    return inner

f1 = outer(f1)
f1()

七、 装饰器语法糖

作用:语法糖仅仅是让代码变得更加简洁

语法糖内部原理:1.使用的时候最好紧跟在被装饰对象的上方

                             2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用

def outer(func_name):  # 装饰器
    def inner(*args, **kwargs):
        print('执行原函数之前的额外操作')
        res = func_name(*args, **kwargs)
        print('执行原函数之后的额外操作')
        return res
    return inner


@outer  # 等价于f1 = outer(f1)
def f1(*args, **kwargs):  # 原函数
    print('函数f1已运行')
f1()
# 执行函数之前的额外操作
# 函数f1已运行
# 执行函数之后的额外操作

@outer  # 等价于f2 = outer(f2)
def f2(*args, **kwargs):  # 原函数
    print('函数f2已运行')
f2()
# 执行函数之前的额外操作
# 函数f2已运行
# 执行函数之后的额外操作

八、 装饰器修复技术

做到和真的一模一样,但是本质其实没有变

 

 

 

标签:闭包,f1,outer,函数,time,print,装饰,def,14
来源: https://www.cnblogs.com/hbutmeng/p/16024257.html