Day10-生成器和装饰器

文章目录

    day10-生成器和装饰器

    1. 回顾推导式/生成器

    回顾生成器 推导式

    """
        回顾生成器  推导式
    
    """
    from collections.abc import Iterable, Iterator
    
    list1 = [x for x in range(10)]
    print(list1)
    
    gen_1 = (x for x in range(10))
    print(gen_1)
    print(isinstance(gen_1, Iterable)) # 生成器属于可迭代对象
    print(isinstance(gen_1, Iterator)) # 生成器也属于迭代器(因为生成器也具备__next__ 以及 __iter__函数)
    
    for x in gen_1:
        print(x)
    
    
    tup_1 = tuple(x for x in range(10))
    print(tup_1)
    

    2. 创建生成器

    生成器 可以理解为懒加载式的函数 + 迭代器的 组合体

    懒:表示更晚的加载时机 等待真正使用的时候才获取

    yield 关键字 可以书写多个 表示暂停当前函数 并且返回yield关键字之后的值 等待下次继续往下执行 直到下一个yield 或者函数结束

    return 只能写一个 结束函数并且返回值

    LazyLoad : 是软件开发常见的一种编程设计思想 表示当我们真正需要使用数据的时候 才获取数据 而不是 一开始就获取数据

    """
        生成器
        可以理解为懒加载式的函数 + 迭代器的 组合体
    
        懒:表示更晚的加载时机  等待真正使用的时候才获取
    
        yield 关键字 可以书写多个 表示暂停当前函数 并且返回yield关键字之后的值
        等待下次继续往下执行 直到下一个yield 或者函数结束
    
        return 只能写一个 结束函数并且返回值
    
        LazyLoad : 是软件开发常见的一种编程设计思想  表示当我们真正需要使用数据的时候 才获取数据
        而不是 一开始就获取数据
    
    """
    
    def func1():
        yield 1
        yield 2
        yield 3
    
    def func2():
        return 1
        return 2
        return 3
    
    
    gen_1 = func1()
    
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    # print(next(gen_1)) # 这里依然报错StopIteration 因为底层依然属于迭代操作 跟迭代报错信息是一致的
    
    
    print("func2函数返回值:",func2())
    print("func2函数返回值:",func2())
    print("func2函数返回值:",func2())
    
    
    

    3. 生成器和普通函数获取数据对比

    普通函数生成器
    内存占用较大极小
    加载方式立即加载懒加载
    速度必须等待整个函数执行完毕 速度慢无需等待整个函数执行完毕 速度快
    无限操作不支持支持

    生成器对比普通函数获取多个数据

    通过这个案例再次感受生成器按需取用的特点

    需求:获取 100万个整数 保存到一个list列表中

    """
        生成器对比普通函数获取多个数据
    
        通过这个案例再次感受生成器按需取用的特点
    
        需求:获取    100万个整数 保存到一个list列表中
    """
    
    def get_list():
        list1 = []
        for i in range(1000000):
            list1.append(i)
        return list1
    
    def create_list():
        for i in range(1000000):
            yield i
    
    list1 = get_list() # 一次性获取100万个数据
    print(list1)
    
    
    gen_1 = create_list() # 这里只是得到了生成器
    print(gen_1)
    print(next(gen_1)) # 每next一次 得到一次数据
    print(next(gen_1)) # 每next一次 得到一次数据
    print(next(gen_1)) # 每next一次 得到一次数据
    print(next(gen_1)) # 每next一次 得到一次数据
    
    

    4. 使用生成器打印斐波那契数列

    """
        使用生成器斐波那契数列
    """
    
    def fibo(): # 这里指定形参
        a , b = 0 , 1
        while True: # 这里书写为有固定次数的循环
            yield a
            a , b = b , a + b
    
    
    gen_1 = fibo()
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    print(next(gen_1))
    

    5.send函数

    send()函数介绍 next(gen)唤醒生成器 并且执行到yield的位置 得到结果 gen.send(param) param表示我们通过send函数向生成器发送的值 这个值传递给了yield 同时yield本身是具备返回值的能力的 所以send函数的效果:发送数据 并且得到yield返回的数据

    使用send函数之前 必须通过next()唤醒生成器 不能不唤醒的情况下 直接调用 send

    """
        send()函数介绍
        next(gen)唤醒生成器 并且执行到yield的位置 得到结果
        gen.send(param) param表示我们通过send函数向生成器发送的值 这个值传递给了yield
        同时yield本身是具备返回值的能力的
        所以send函数的效果:发送数据 并且得到yield返回的数据
    
        使用send函数之前 必须通过next()唤醒生成器 不能不唤醒的情况下 直接调用 send
    
        需求:通过send函数发送数据 从指定数据的位置往后继续打印斐波那契数列 比如 10 11 21 32 53 ……
        10 1 11 12 33
        如果不传参参数 那么依然按照 0 1 1 2 3 5 8  这种格式打印 即从头开始
    """
    
    def fibonacci():
        a , b = 0, 1
        while True:
            result = yield a # 这里通过result接收 接收的是通过send函数传递的值
            if result is not None:
                a , b = result , result + 1
            else:
                a, b = b , a + b
    
    
    gen_1 = fibonacci()
    
    print(next(gen_1)) # 没有next 直接开始send 将会报错
    
    print(gen_1.send(10))
    
    print(next(gen_1))
    
    print(gen_1.send(55))
    
    print(next(gen_1))
    
    print(next(gen_1))
    
    
    
    
    

    gen.send(None) 这种操作属于唤醒生成器

    """
        gen.send(None) 这种操作属于唤醒生成器
    """
    
    def fibonacci():
        a , b = 0, 1
        while True:
            result = yield a # 这里通过result接收 接收的是通过send函数传递的值
            if result is not None:
                a , b = result , result + 1
            else:
                a, b = b , a + b
    
    
    gen_1 = fibonacci()
    
    print(gen_1.send(None))
    
    print(next(gen_1))
    
    print(next(gen_1))
    
    print(next(gen_1))
    
    
    
    
    

    6. 闭包

    闭包 ​ 1.嵌套函数 ​ 2.内层函数使用到外层函数的变量 ​ 3.在外层函数中将内层函数对象 作为返回值返回

    闭包的作用: ​ 1.简化调用过程 ​ 2.封装底层实现细节(安全 可读性 可维护性 )

    """
        闭包
            1.嵌套函数
            2.内层函数使用到外层函数的变量
            3.在外层函数中将内层函数对象 作为返回值返回
    
    
        闭包的作用:
            1.简化调用过程
            2.封装底层实现细节(安全 可读性 可维护性  )
    """
    
    
    # 为什么要使用闭包?
    # 根据倍率计算数值
    
    def calc_rate_num(num, rate):
        return num * rate
    
    
    result1 = calc_rate_num(1, 2)
    print(result1)
    result2 = calc_rate_num(2, 2)
    print(result2)
    result3 = calc_rate_num(3, 2)
    print(result3)
    result4 = calc_rate_num(4, 2)
    print(result4)
    result5 = calc_rate_num(5, 2)
    print(result5)
    
    
    print("-" * 50)
    
    
    def outer_rate_calc(rate):
        def inner_rate_calc(num):
            return num * rate
        return inner_rate_calc
    
    inner_calc1 = outer_rate_calc(2)
    print(inner_calc1(3))
    print(inner_calc1(4))
    print(inner_calc1(5))
    
    print("-" * 50)
    
    inner_calc2 = outer_rate_calc(3)
    print(inner_calc2(4))
    print(inner_calc2(5))
    print(inner_calc2(6))
    print(inner_calc2(7))
    
    

    需求:某公司要根据员工的入职年限计算年终奖 比如1年 1个月薪资年终级 2年两个月 以此类推 但是考虑到薪酬保密制度 所以每个人只需要传入对应的入职年限即可 不需要传入每个人的薪资

    """
        需求:某公司要根据员工的入职年限计算年终奖 比如1年 1个月薪资年终级  2年两个月 以此类推
        但是考虑到薪酬保密制度 所以每个人只需要传入对应的入职年限即可  不需要传入每个人的薪资
    """
    
    emp_dict = {"001" : 10000,"002" : 20000 , "003" : 30000 , "004" : 40000 , "005" : 50000 }
    
    
    def outer_bonus(join_years):
        def inner_bonus(emp_id):
            return join_years * emp_dict.get(emp_id)
        return inner_bonus
    
    
    inner_calc = outer_bonus(5)
    print(inner_calc("001"))
    print(inner_calc("002"))
    print(inner_calc("003"))
    
    
    
    
    
    
    
    

    7. 装饰器

    7.1 装饰器介绍

    装饰器 POP 面向过程 OOP 面向对象 FP 面向函数 AOP 面向切面 代理思想 面向切面表示针对于整个业务逻辑中 各个相关的时机 之前 之后 之中 报错的情况 等等 可以有第三方(代理方)帮我们去做一些共性的操作 处理

    将共性的操作提取出来 封装为一个函数 最终我们只关注自身的业务逻辑 只不过这些函数不需要我们自己再进行调用了 而是交给第三方调用

    """
        装饰器
        POP 面向过程
        OOP 面向对象
        FP 面向函数
        AOP 面向切面 代理思想  面向切面表示针对于整个业务逻辑中 各个相关的时机 之前 之后 之中 报错的情况 等等
        可以有第三方(代理方)帮我们去做一些共性的操作 处理
    
        将共性的操作提取出来 封装为一个函数 最终我们只关注自身的业务逻辑 只不过这些函数不需要我们自己再进行调用了
        而是交给第三方调用
    
    
    
    """
    
    
    def open_conn():
        print("创建、打开  数据库连接")
    
    def close_conn():
        print("关闭、销毁  数据库连接")
    
    
    def add():
        open_conn()
        print("执行数据库添加操作")
        close_conn()
    
    def remove():
        open_conn()
        print("执行数据库删除操作")
        close_conn()
    
    def update():
        open_conn()
        print("执行数据库修改操作")
        close_conn()
    
    def get():
        open_conn()
        print("执行数据库查询操作")
        close_conn()
    
    add()
    remove()
    update()
    get()
    

    7.2 闭包的形式优化代码

    使用装饰器 (使用AOP思想进行优化) 回顾闭包: ​ 1.嵌套函数 ​ 2.内层函数调用外层函数的变量 ​ 3.返回内层函数 装饰器和闭包的规则基本是一致的 不同之处在于 闭包外层函数的变量可以是任何类型 而装饰器外层函数的变量 必须是一个函数对象

    """
        使用装饰器 (使用AOP思想进行优化)
        回顾闭包:
            1.嵌套函数
            2.内层函数调用外层函数的变量
            3.返回内层函数
        装饰器和闭包的规则基本是一致的 不同之处在于 闭包外层函数的变量可以是任何类型
        而装饰器外层函数的变量 必须是一个函数对象
    """
    
    
    
    def conn_decorator(func):
        def wrapper():
            print("开启数据库连接")
            func()
            print("关闭数据库连接")
        return wrapper
    
    def add():
        print("执行数据库添加操作")
    
    def remove():
        print("执行数据库删除操作")
    
    def update():
        print("执行数据库修改操作")
    
    def get():
        print("执行数据库查询操作")
    
    
    # 没有使用装饰器 只是使用了装饰器思想的调用方式 很麻烦
    add_dec = conn_decorator(add)
    add_dec()
    
    remove_dec = conn_decorator(remove)
    

    7.3 装饰器的方式优化代码

    """
        使用装饰器 (使用AOP思想进行优化) 使用AOP增强功能
        回顾闭包:
            1.嵌套函数
            2.内层函数调用外层函数的变量
            3.返回内层函数
        装饰器和闭包的规则基本是一致的 不同之处在于 闭包外层函数的变量可以是任何类型
        而装饰器外层函数的变量 必须是一个函数对象
    """
    
    
    
    def conn_decorator(func):
        def wrapper():
            print("开启数据库连接")
            func()
            print("关闭数据库连接")
        return wrapper
    
    @conn_decorator
    def add():
        print("执行数据库添加操作")
    
    @conn_decorator
    def remove():
        print("执行数据库删除操作")
    
    @conn_decorator
    def update():
        print("执行数据库修改操作")
    
    @conn_decorator
    def get():
        print("执行数据库查询操作")
    
    
    add()
    remove()
    update()
    get()
    
    

    7.4 装饰器返回值问题

    关于装饰器返回值问题 如果在原来的函数中是存在返回值的 那么必须在装饰器函数中继续处理返回值 否则 返回值将丢失

    总结:装饰器中都要接收并且返回数据 以防止数据的丢失(即使原函数没有返回值也无妨 因为接收了None 返回None)

    """
        关于装饰器返回值问题
        如果在原来的函数中是存在返回值的 那么必须在装饰器函数中继续处理返回值 否则 返回值将丢失
    
        总结:装饰器中都要接收并且返回数据 以防止数据的丢失(即使原函数没有返回值也无妨 因为接收了None 返回None)
    """
    
    
    def conn_decorator(func):
        def wrapper():
            print("开启数据库连接")
            result = func()
            print("关闭数据库连接")
            return result
        return wrapper
    
    @conn_decorator
    def add():
        print("执行数据库添加操作")
        return 100
    
    
    print(add())
    
    
    

    7.5 装饰器参数问题

    关于装饰器处理函数 参数的问题 被装饰的函数可能会存在参数 那么我们在装饰的函数中应该继续携带参数 进行传递 否则 参数将丢失

    """
        关于装饰器处理函数 参数的问题
        被装饰的函数可能会存在参数 那么我们在装饰的函数中应该继续携带参数 进行传递 否则 参数将丢失
    """
    
    def conn_decorator(func):
        def wrapper(*args,**kwargs):
            print("开启数据库连接")
            print(args)
            print(kwargs)
            result = func(*args,**kwargs)
            print("关闭数据库连接")
            return result
        return wrapper
    
    @conn_decorator
    def add(a,b,c):
        print("执行数据库添加操作")
        return 100
    
    
    print(add(1,2,c = 3))
    

    8. 多层装饰器

    多层装饰器 一个函数上方可以添加多个装饰器

    实际开发中我们的程序都要记录日志,日志有各种工具实现,不需要人为实现 日志的级别:INFO ERROR WARNING

    以下代码为反例:这样书写的代码很啰嗦 冗余 重复(模板)代码太多 代码可读性很差

    """
        多层装饰器 一个函数上方可以添加多个装饰器
    
        实际开发中我们的程序都要记录日志,日志有各种工具实现,不需要人为实现
        日志的级别:INFO ERROR WARNING
    
        以下代码为反例:这样书写的代码很啰嗦 冗余 重复(模板)代码太多 代码可读性很差
    
    """
    
    
    def open_conn():
        print("创建、打开  数据库连接")
    
    def close_conn():
        print("关闭、销毁  数据库连接")
    
    
    def add():
        print("add函数2026年6月8日14:48:57执行了")
        open_conn()
        print("执行数据库添加操作")
        close_conn()
        print("add函数2026年6月8日14:49:07执行完毕")
    
    
    
    add()
    
    

    多层装饰器 一个函数上方可以添加多个装饰器

    实际开发中我们的程序都要记录日志,日志有各种工具实现,不需要人为实现 日志的级别:INFO ERROR WARNING

    装饰的顺序:从下往上 执行的顺序:从上往下

    """
        多层装饰器 一个函数上方可以添加多个装饰器
    
        实际开发中我们的程序都要记录日志,日志有各种工具实现,不需要人为实现
        日志的级别:INFO ERROR WARNING
    
        以下代码为反例:这样书写的代码很啰嗦 冗余 重复(模板)代码太多 代码可读性很差
    
        装饰的顺序:从下往上
        执行的顺序:从上往下
    
    """
    import time
    
    
    def conn_decorator(func):
        def conn_inner(*args,**kwargs):
            print("开启数据库连接1")
            result = func(*args,**kwargs) # 这里装饰add函数
            print("关闭数据库连接2")
            return result
        return conn_inner # 返回装饰之后的add函数
    
    
    def log_decorator(func): # 将装饰之后的add函数作为参数传入
        def log_inner(*args,**kwargs):
            print(f"{func.__name__}在{time.time()}开始执行3")
            result = func(*args,**kwargs) # 处理装饰之后的add函数
            print(f"{func.__name__}在{time.time()}执行完毕4")
            return result
        return log_inner
    
    @log_decorator
    @conn_decorator
    def add():
        print("执行数据库添加操作")
    
    
    add()
    
    
    

    9.带参数的装饰器

    带参数的装饰器 必须有三层函数嵌套 最外层函数 负责处理参数 中间层 负责接收被装饰的函数 最内层 装饰函数

    """
        带参数的装饰器 必须有三层函数嵌套
        最外层函数 负责处理参数
        中间层  负责接收被装饰的函数
        最内层 装饰函数
    """
    import time
    def log_decorator(level):
        def acc_func(func):
            def log_inner(*args,**kwargs):
                print(f"日志等级【{level}】:{func.__name__}在{time.time()}开始执行3")
                result = func(*args,**kwargs) # 处理装饰之后的add函数
                print(f"日志等级【{level}】:{func.__name__}在{time.time()}执行完毕4")
                return result
            return log_inner
        return acc_func
    
    
    @log_decorator(level="ERROR")
    def add():
        print("执行数据库添加操作")
    
    
    add()
    

    10. 类装饰器

    类装饰器 本身就属于一个类了 类中可以定义变量 保存更多的数据 类装饰器的优势:存变量 存配置 作缓存 作计数等等

    需要定义两个函数 ​ __ init __ 用于接收被装饰的函数对象 ​ __ call __ 用于装饰函数

    """
        类装饰器 本身就属于一个类了
        类中可以定义变量 保存更多的数据
        类装饰器的优势:存变量 存配置 作缓存 作计数等等
    
        需要定义两个函数
            __init__ 用于接收被装饰的函数对象
            __call__ 用于装饰函数
    
    """
    
    class Logger:
        def __init__(self,func):
            self.func = func
    
        def __call__(self,*args,**kwargs):
            print(f"{self.func.__name__}执行了")
            result = self.func(*args,**kwargs)
            print(f"{self.func.__name__}执行完毕了")
            return result
    
    @Logger
    def add():
        print("执行数据库添加操作")
    
    add()
    

    当前案例用于展示类装饰器的特殊之处 存计数器

    """
        当前案例用于展示类装饰器的特殊之处
        存计数器
    """
    
    class CounterDecorator:
        def __init__(self,func):
            self.func = func
            self.count = 0 # 初始化一个属性 初始值为0
    
    
        def __call__(self,*args,**kwargs):
            self.count += 1
            print(f"{self.func.__name__}执行了")
            result = self.func(*args, **kwargs)
            print(f"统计次数:{self.count}")
            print(f"{self.func.__name__}执行完毕了")
            return result
    
    
    @CounterDecorator
    def add():
        print("执行数据库添加操作")
    
    
    add()
    add()
    add()
    
    end