迭代器与生成器

迭代器入门

python 复制代码
"""
案例: 演示自定义迭代器.

迭代器介绍:
    概述:
        自定义的类, 只要重写了 __iter__() 和 __next__() 方法, 就可以称为 迭代器.
    目的:
        隐藏底层的逻辑, 让用户使用更方便.
        惰性加载, 用的时候才会获取.

回顾: for循环格式
    for i in 可迭代类型:
        pass
"""
# 需求: 模拟range(1, 6), 自定义 迭代器实现同等逻辑.
# 场景1: 回顾 range()用法.
for i in range(1, 6):
    print(i)
print('-' * 23)


# 场景2: 自定义迭代器.
# 1. 自定义 迭代器类.
class MyIterator:
    # 2. 通过init魔法方法, 初始化属性, 指定: 范围.
    def __init__(self, start, end):
        self.current_value = start      # 当前值, 默认为 开始值.
        self.end = end                  # 结束值.

    # 3. 重写iterator魔法方法, 返回当前对象(即: 迭代器对象).
    def __iter__(self):
        return self

    # 4. 重写next魔法方法, 返回当前值, 并更新当前值.
    def __next__(self):
        # 4.1 判断当前值范围是否合法.
        if self.current_value >= self.end:
            raise StopIteration     # 抛出异常, 迭代结束.

        # 4.2 走这里, 说明当前值合法, 返回当前值, 并更新当前值.
        # value = self.current_value      # value =               1   2   3   4   5
        # self.current_value += 1         # self.current_value =  2   3   4   5   6
        # return value                    #                       1   2   3   4   5

        # 效果同上, 代码更简单
        self.current_value += 1          # self.current_value =  2   3   4   5   6
        return self.current_value - 1    #                       1   2   3   4   5


# 5. 创建迭代器对象, 并遍历.
# 5.1 for循环
for i in MyIterator(1, 6):
    print(i)
print('-' * 23)

# 5.2 next()函数
my_itr = MyIterator(10, 13)
print(next(my_itr)) # 10
print(next(my_itr)) # 11
print(next(my_itr)) # 12
# print(next(my_itr)) # raise StopIteration     # 抛出异常, 迭代结束.

生成器介绍

  • 案例1: 推导式写法

    python 复制代码
    """
    案例: 演示生成器之 推导式写法.
    
    生成器介绍:
        概述:
            所谓的生成器就是基于 数据规则, 用一部分在生成一部分, 而不是一下子生成完所有.
        目的:
            可以节省大量的内存.
        实现方式:
            1. 推导式写法.
            2. yield关键字
    """
    import sys      # system: 系统模块
    
    # 场景1: 生成器 推导式写法.
    # 需求1: 生成1 ~ 10之间的整数.
    my_generator = (i for i in range(1, 11)) #这里使用的是()
    print(my_generator)
    print(type(my_generator))   # <class 'generator'>
    print('-' * 23)
    
    # 需求2: 生成 1 ~ 10 之间的偶数.
    my_gt2 = (i for i in range(1, 11) if i % 2 == 0)
    print(my_gt2)
    print('-' * 23)
    
    # 需求3: 如何从生成器中获取数据.
    # 思路1: next()
    print(next(my_gt2))     # 2
    print(next(my_gt2))     # 4
    print('*' * 23)
    for i in my_gt2:
        print(i)            # 6, 8, 10
    print('-' * 23)
    
    # 验证 生成器的目的 就是可以减少内存占用.
    my_list = [i for i in range(1000000)]
    my_gt3 = (i for i in range(1000000))
    print(type(my_list), type(my_gt3))
    
    
    # 查看my_list的内存空间占用.
    print(f'my_list的内存占用: {sys.getsizeof(my_list)}')    # 89095160
    print(f'my_gt3的内存占用: {sys.getsizeof(my_gt3)}')      # 192
    print('-' * 23)
  • 案例2: yield关键字

    python 复制代码
    """
    案例: 演示生成器之 推导式写法.
    
    生成器介绍:
        概述:
            所谓的生成器就是基于 数据规则, 用一部分在生成一部分, 而不是一下子生成完所有.
        目的:
            可以节省大量的内存.
        实现方式:
            1. 推导式写法.
            2. yield关键字
    """
    
    # 需求: 通过yield方式, 获取到生成器之 1 ~ 10之间的整数.
    # 回顾: 推导式写法.
    my_g = (i for i in range(1, 11))
    
    # yield方式如下.
    # 1.定义函数, 存储到生成器中, 并返回.
    def my_fun():
        # yield在这里做了三件事儿: 1.创建生成器对象.  2.把值存储到生成器中.  3.返回生成器.
        for i in range(1, 11):
            yield i
    
    # 2.测试.
    my_g2 = my_fun()
    print(type(my_g2))  # <class 'generator'>
    
    print(next(my_g2))
    print(next(my_g2))
    print('-' * 23)
    for i in my_g2:
        print(i)
  • 案例3: 批量歌词

    python 复制代码
    """
    案例: 基于传入的数值(每批次的歌词条数), 创建 生成器, 生成批次歌词.
    """
    import math
    
    # 需求: 基于文件中 周杰伦的歌词, 创建生成器, 根据传入的每批次的歌词条数, 生成歌词批次.
    # 1. 定义函数, 接收 每批次的歌词条数, 返回生成器.
    def dataset_loader(batch_size):     # 假设是 8条/批次
        """
        自定义的 歌词 批量生成器
        :param batch_size:  每批次的歌词条数
        :return: 生成器, 每个元素都是一批次的数据, 例如: (8条, 8条, 8条...)
        """
        # 1.1 读取文件数据.
        with open('./data/jaychou_lyrics.txt', 'r', encoding='utf-8') as src_f:
            # 1.2 一次读取所有行.
            # lines = [line.strip() for line in src_f.readlines()]
            lines = src_f.readlines()
    
            # 1.3 计算批次总数, 假设: 5批
            total_batch = math.ceil(len(lines) / batch_size)
    
            # 1.4 for循环方式, 获取到每批次的数据, 放到生成器中, 并返回.
            for idx in range(total_batch):      # idx的值: 0, 1, 2, 3, 4
                # 第1批歌词, 批次索引(idx=0), 歌词为: 第1条 ~ 第8条, 索引为: 0 ~ 7
                # 第2批歌词, 批次索引(idx=1), 歌词为: 第9条 ~ 第16条, 索引为: 8 ~ 15
                # 第3批歌词, 批次索引(idx=2), 歌词为: 第17条 ~ 第24条, 索引为: 16 ~ 23
                yield lines[idx * batch_size : idx * batch_size + batch_size]      # 第1批
    
    
    # 2. 测试.
    dl = dataset_loader(8)
    print(next(dl)) # 第1批
    print(next(dl)) # 第2批
    
    for batch_data in dl:
        print(batch_data)

Property属性介绍

  • 场景1: 装饰器用法

    python 复制代码
    """
    案例: 演示property属性的用法.
    
    property属性介绍:
        概述/目的/作用:
            把 函数 当做 变量来使用.
        实现方式:
            方式1: 装饰器.
            方式2: 类属性.
    
    
    property的装饰器用法:
        @property               修饰 获取值的函数
        @获取值的函数名.setter     修饰 设置值的函数
    
        之后, 就可以直接 .上述的函数名 来当做变量直接用.
    """
    
    # 需求: 定义学生类, 私有属性 age, 通过property实现简化调用.
    # 1. 定义学生类.
    class Student:
        # 1.1 私有属性.
        def __init__(self):
            self.__age = 18
    
        # 1.2 提供公共的方式方式
        @property
        def age(self):
            return self.__age
    
        @age.setter
        def age(self, age):
            # 可以在这里对传入的age值做判断, 但是一般不做, 重要字段才会做判断.
            # 因为实际开发中数据是从前端传过来的, 已经做过判断了, 这里做属于二次校验.
            self.__age = age
    
    # 2. 测试
    if __name__ == '__main__':
        # 2.1 创建学生对象.
        s = Student()
        # 2.2. 设置值
        s.age = 20
        # 2.3 获取值
        print(s.age)
  • 场景2: 类属性

    python 复制代码
    """
    案例: 演示property属性的用法.
    
    property属性介绍:
        概述/目的/作用:
            把 函数 当做 变量来使用.
        实现方式:
            方式1: 装饰器.
            方式2: 类属性.
    
    
    property的装饰器用法:
        @property               修饰 获取值的函数
        @获取值的函数名.setter     修饰 设置值的函数
    
    property类属性的用法:
        类属性名 = property(获取值的函数名, 设置值的函数名)
    
        之后, 就可以直接 .上述的函数名 来当做变量直接用.
    """
    
    # 需求: 定义学生类, 私有 age属性, 通过property充当类属性用.
    # 1. 定义学生类.
    class Student:
        # 1.1 私有age属性.
        def __init__(self):
            self.__age = 20
    
        # 1.2 公共的访问方式.
        def get_age(self):
            return self.__age
    
        def set_age(self, age):
            self.__age = age
    
        # 1.3 封装上述的公共方式为 类属性
        # 参1: 获取值的函数名,    参2: 设置值的函数名
        age = property(get_age, set_age)
    
    # 2. 测试
    if __name__ == '__main__':
        # 2.1 创建学生对象.
        s = Student()
        # 2.2. 设置值
        s.age = 99
        # 2.3 获取值
        print(s.age)
相关推荐
码农水水2 小时前
京东Java面试被问:分布式会话的一致性和容灾方案
java·开发语言·数据库·分布式·mysql·面试·职场和发展
deephub2 小时前
用 PydanticAI 让 LLM 输出变成可信赖的 Python 对象
人工智能·python·大语言模型·agent
郝学胜-神的一滴2 小时前
Linux网络字节序详解:从理论到实践
linux·服务器·c语言·开发语言·c++·网络协议·程序人生
带带弟弟学爬虫__2 小时前
速通新Baidu Frida检测
前端·javascript·vue.js·python·网络爬虫
b2077212 小时前
Flutter for OpenHarmony 身体健康状况记录App实战 - 运动分析实现
python·flutter·harmonyos
Tansmjs2 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
2401_841495642 小时前
【操作系统】进程的算法
python·算法·操作系统·进程·进程调度算法·进程同步与互斥算法·死锁处理算法
夏幻灵2 小时前
Java中的this关键字解析与应用
java·开发语言·python
移幻漂流2 小时前
JNI的本质解析:Android Framework视角下的Java-Native衔接机制
android·java·开发语言