闭包函数与装饰器

1. 闭包函数

# 闭包函数的两大特征
    1.闭:定义在函数内部的函数
    2.包:内部函数使用了外层函数名称空间中的名字
 
def print_python_cat():
    index = 999
    def python_cat():
        print('from print_python_car>>>python_ca',index)
    return python_cat
index = 666
res = print_python_cat()
res()
# >>>:999

 

2. 闭包函数实际应用

# 闭包函数是给函数体传参的另外一种方式
# 函数体传参的方式1:通过形参进行传值
def print_python_cat(username):
    print(username)
# 函数体代码需要什么就可以在形参中写什么
print_python_cat('Python Cat')

# 函数体传参的方式2:闭包
# 1. 无参闭包函数
def print_python_cat():
    username = 'Python Cat'
    def python_cat():
        print(username)  # 永远使用的都是Python Cat
    return python_cat
res = print_python_cat()
res()
# >>>:Python Cat

# 2. 有参闭包函数
def print_python_cat(Python_Cat):
    def python_cat():
        print(Python_Cat)  # 永远使用的都是jason
    return python_cat
res = print_python_cat('Python Cat')
# 形参Python_Cat与值Python Cat临时绑定 >>>:print_python_cat局部名称空间中
res()
# >>>:Python Cat

res1 = print_python_cat('python cat')
# 形参Python_Cat与值python cat临时绑定 >>>:print_python_cat局部名称空间中
res1()
# >>>:python cat

 

3.装饰器简介

"""
装饰器并不是一个新的知识点 而是由前两天所有的函数知识点整合到一起的产物
    名称空间 函数名 闭包函数...
"""
# 装饰器的本质
# 在不改变被装饰对象原有的'调用方式'和'内部代码'的情况下给被装饰对象添加新的功能
#
# 装饰器的原则
# 1. 对扩展开放
# 2. 对修改封闭

# 导入time模块
import time

print(time.time())  # 1647589014.4316216
'''上述的数字是时间戳:1970年1月1日0时0分0秒距离刚刚代码运行间隔的秒数'''
time.sleep(3)  # 让程序原地停止三秒后,继续执行
print('hello world')


# 需求:统一函数的执行时间
import time


def cat():
    time.sleep(3)  # 让程序原地停止三秒后,继续执行
    print('from index')


'''给index函数增加了一个统计执行时间的功能'''
start_time = time.time()  # 函数执行之前获取一个时间戳
cat()
end_time = time.time()  # 函数执行之后获取一个时间戳
print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间

# 可以改为装饰器
def python_cat():
    def print_python_cat():
        start_time = time.time()  # 函数执行之前获取一个时间戳
        cat()
        end_time = time.time()  # 函数执行之后获取一个时间戳
        print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间
    return print_python_cat

 

4.简易版本装饰器

import time
def print_python_cat():
    time.sleep(1)
    print('from print_python_cat')
'''给index函数增加了一个统计执行时间的功能'''
start_time = time.time()  # 函数执行之前获取一个时间戳
print_python_cat()
end_time = time.time()  # 函数执行之后获取一个时间戳
print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间

'''
1.思考:如果在很多地方都需要调用print_python_cat 如何统计print_python_cat执行时间
    在很多地方都需要执行统计print_python_cat函数执行时间的代码
        在不同的地方需要执行相同的代码      >>>:       函数
    
    直接通过传参的方式
    缺陷1:
        代码固定死了 无法统计其他函数的执行时间
        能否解决?
            可以!  将函数名通过形参的形式传入
    缺陷2:
        封装成函数之后 调用方式改变了 不符合装饰器原则
        能否解决?
            无法进行解决
'''
def print_python_cat(func):
    start_time = time.time()  # 函数执行之前获取一个时间戳
    func()
    end_time = time.time()  # 函数执行之后获取一个时间戳
    print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间

def home():
    time.sleep(3)
    print('from home')

print_python_cat(print_python_cat)
print_python_cat(home)

"""
第一种直接给函数体传参的方式无法实现装饰器
    只能采用第二种给函数体传参的方式试试看了
"""
import time
def print_python_cat():
    time.sleep(1)
    print('from print_python_cat')

def home():
    time.sleep(3)
    print('from home')
    
print(home)
def outer(func):  # 真正的index被outer局部名称空间存储了
    def get_time():
        start_time = time.time()  # 函数执行之前获取一个时间戳
        func()  # 调用了真正的index函数
        end_time = time.time()  # 函数执行之后获取一个时间戳
        print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间
    return get_time
res = outer(print_python_cat)  # 左侧的res就是一个普通的变量名
res()
a = outer(print_python_cat)  # 左侧的a就是一个普通的变量名
a()
b = outer(print_python_cat)  # 左侧的b就是一个普通的变量名
b()
print_python_cat = outer(print_python_cat)
print_python_cat()  # 看似调用的index其实调用的是get_time
print(print_python_cat)  # 全局名称空间中的index指向的是get_time函数体代码
home = outer(home)  # 狸猫换太子

 

5. 进阶版本装饰器

# 解决的是参数问题
def print_python_cat(func_name):
    def python_cat(*args, **kwargs):
        start_time = time.time()
        func_name(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return python_cat

 

6. 完整版本装饰器

# 解决的是返回值问题
def print_python_cat(func_name):
    def python_cat(*args, **kwargs):
        start_time = time.time()
        res = func_name(*args, **kwargs)  # 执行真正的index函数
        end_time = time.time()
        print(end_time - start_time)
        # return '不要急躁' # 如何在此处返回真正index函数的返回值
        return res
    return python_cat

 

7. 装饰器模板

'''编写装饰器其实有一套固定的代码 不需要做任何理解'''
def outer(func_name):  # func_name用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        print('执行被装饰函数之前 可以做的额外操作')
        res = func_name(*args, **kwargs)  # 执行真正的被装饰函数
        print('执行被装饰函数之后 可以做的额外操作')
        return res  # 返回真正函数的返回值
    return inner

 

8.装饰器语法糖

# 仅仅是让代码编写的更加好看、简洁!!!
# Author:Jason

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

@outer  # 等价于 index = outer(index)
def index(*args, **kwargs):
    print('from index')
# index = outer(index)  # 总感觉这一行代码有点low!!!

@outer  # 等价于 home = outer(home)
def home(*args,**kwargs):
    print('from home')


print(index)
print(home)

"""
语法糖内部原理
    1.使用的时候最好紧跟在被装饰对象的上方
    2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用

ps:针对之前的周末大作业 只需要单独写一个装饰器 之后使用语法糖装饰即可
"""

 

9. 装饰器修复技术

# 做到比真的还要真  但是本质其实没有变
# Author:Jason
from functools import wraps
def outer(func_name):
    @wraps(func_name)
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的操作')
        res = func_name(*args, **kwargs)
        return res
    return inner
@outer
def index():
    print('from index')
@outer
def home():
    '''这是home函数的注释'''
    print('from home')
# help(home)  # help可以查看指定函数的注释信息
# help(len)
# index()  # 目前的以假乱真 有瑕疵
# print(index)
# help(home)
# help(index)
# print(home)
# print(index)
home()
index()

 

posted @ 2022-03-18 16:53  thrombus  阅读(50)  评论(0)    收藏  举报