迭代器与生成器

82 阅读2分钟

可迭代对象

1.迭代就是更新换代,每次迭代都必须基于上一次的迭代成果,单纯的重复不是迭代
2.代码展示
 	# 不属于迭代
    while True:
        print('嘿嘿嘿')
    # 属于迭代
    n = 0
    while n < 10:
        print(n)
        n += 1
3.如和判断可迭代对象
	3.1.'内置__iter__方法的都可以叫可迭代对象'读作:双下iter方法
	3.2.int float bool 以及函数名都不是可迭代对象
	3.3.字符串,列表,字典,元组,集合,文件都是可迭代对象,其中文件还是迭代器对象

迭代器对象

1.作用
    迭代器对象为我们提供了一种不依赖索引的取值方式,因此我们可以对字典、集合这种无序类型循环取值。
2.如何判断迭代器对象
    内置有__iter__和__next__方法的成为迭代器对象
3.可迭代对象与迭代器对象的关系
    可迭代对象调用__iter__方法之后就会变成迭代器对象
    迭代器对象调用__iter__方法无论多少次还是迭代器对象本身
4.迭代器对象迭代取值
	s1 = 'abc'  # s1本就是可迭代对象
    s1 = s1.__iter__() # 将s1变为迭代器对象
    print(s1.__next__()) # a
    print(s1.__next__()) # b
    print(s1.__next__()) # c
    print(s1.__next__()) # 取不到值,直接报错
    
    l1 = [11, 22, 33, 44, 55, 66, 77, 88]
    # 需求:不使用for循环 依次打印出列表中所有的数据值
    # 1.先将列表变成迭代器对象
    res = l1.__iter__()
    # 2.定义一个计数器
    count = 0
    # 3.编写while循环
    while count < len(l1):
        print(res.__next__())
        count += 1
6.迭代器反复使用
    l1 = [1,2,3,4]
    print(l1.__iter__().__next__())  # 1
    print(l1.__iter__().__next__())  # 1
    print(l1.__iter__().__next__())  # 1
	# 每次都是产生了一个新的迭代器对象,所以每次输出都是一样的值
    l1 = l1.__iter__()
    print(l1.__iter__().__next__())  # 1
    print(l1.__iter__().__next__())  # 2
    print(l1.__iter__().__next__())  # 3
    # 每次使用的都是同一个迭代器对象
7.简写
    res = l1.__iter__()  ===>  iter(l1)
    res.__next__()  ===>  next(res)
8.补充说明
    可迭代对象和迭代器对象无法通过打印看到其内部信息
    这时候他们都能够帮你节省内存,相当于是一个临时工厂,你要一个数据就给一个。

迭代取值与索引取值的差异

l1 = [1,2,3,4,5]
#索引取值
print(l1[0])
print(l1[1])
print(l1[2])
print(l1[4])
'优势:可以随意反复地获取任意数据值(自由)'
'劣势:针对无序的容器类型无法取值(类型限制)'
#迭代取值
res = l1.__iter__()
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
'优势:可以对无序的容器类型取值(通用)'
'劣势:取值只能一直向前,不能回退(不可逆)'

for循环的本质

语法结构
    for 变量名 in 可迭代对象:
        for循环体代码
1.for会自动将in后面的数据调用__iter__()变成迭代器对象
2.之后每次循环调用__next__()取值
3.最后没有值__next__()会报错 for能够自动处理该错误 让循环正常结束

生成器对象

1.本质就是迭代器对象(内置__iter__ 和 __next__方法),只不过迭代器是python解释器提供给我们的(现成的),生成器是我们自己定义的。
2.生成器的主要作用是节省代码
	一种不依赖索引的取值方式
    可以节省数据类型占用的内存空间(主要)
3.生成器对象代码实现    
    def index():
        print('我是谁呀 嘿嘿嘿~')
        yield 111, 222, 333
        print('你追我 如果你追到我 我就... 嘿嘿嘿~')
        yield 222
        print('实在跑不动了 嘿嘿嘿~')
        yield 333
	"""
	当函数体代码中有yield关键字
	函数名第一次加括号调用不会执行函数体代码
	而是由普通的函数变成了迭代器对象(生成器)
	"""   
    print(index)  # <function index at 0x000001E30205F0D0>
	res = index()
	print(res)  # <generator object index at 0x0000015127BFDC10>  生成器对象
	res.__next__()  # 我是谁呀 嘿嘿嘿~
	res.__next__()  # 你追我 如果你追到我 我就... 嘿嘿嘿~
    """
    yield可以在函数体代码中出现多次
    每次调用__next__方法都会从上到下执行,知道遇到yield停留到此处
    """
    print(res.__next__())  # (333, 222, 111)
    yield后面如果有数据值,则会像return一样返回出去,如果有多个数据值,则会自动组织成元组返回。
 4.生成器对象练习 
# 编写生成器 实现range方法的功能
# 1.先编写两个参数的情况
    def my_range(start_num, end_num):
        while start_num < end_num:
            yield start_num
            start_num += 1
    for i in my_range(1, 10):
        print(i)
# 2.再编写可能有一个参数的情况        
def my_range(start_num, end_num = None):
    if not end_num:
        end_num = start_num
        start_num = 1
    while start_num < end_num:
        yield start_num
        start_num += 1
for i in my_range(10):
    print(i)    
# 3.最后编写三个参数的情况
def my_range(start_num, end_num = None, step = 1):
    if not end_num:
        end_num = start_num
        start_num = 1
    while start_num < end_num:
        yield start_num
        start_num += step
for i in my_range(1,10,2):
    print(i)

yield其他用法

yield不仅能返回值,还可以接收外界传来的值
    def index(name,food=None):
        print(f'{name}准备干午饭!!!')
        while True:
            food = yield
            print(f'{name}正在吃{food}')
    res = index('jason')
    res.__next__()
    res.send('生蚝')  # send方法可以将'生蚝'传值给yield(此时food = '生蚝'),并自动调用__next__方法

生成器表达式

即:"元组生成式"
l1 = (i**2 for i in range(10) if i > 3)
print(l1)  # <generator object <genexpr> at 0x000001A793439C10>