31.Python迭代器&生成器

1.可迭代对象

1.1定义
迭代:没次更新都必须依赖上一次的结果,其目的通常是为了逼近所需目标或结果。

Python中内置__iter__方法都是可迭代对象。
 一般情况下所有的双下方法都会有一个与之对应的简化写法的函数
 
 __iter__ --> iter()
内置的方法可以通过点方法查看。
针对双下划线开头,双下滑线结尾的方法标准的读法是 双下方法名 例 双下iter。
1.2例子
1.非迭代
更新没有依赖上一次的结果.
# 非迭代
num = 0
while True:
    print(num)
2.迭代
在上一次的基础上更新.
num = 0
while True:
    print(num)
    num += 1
1.3可迭代类型
迭代器对象 又称为 迭代器
可迭代对象 又称为 迭代对象

迭代器.__iter__()方法将可迭代对象转成迭代器对象。
可迭代对象.__iter__() --生成--> 迭代器
常用数据类型中:
字符串,列表,字典,元组,集合,文件 都是可迭代对象.
''.__iter__()  # 字符串
[].__iter__()  # 列表
{}.__iter__()  # 字典
tuple().__iter__()  # 元组
set().__iter__()  # 集合
f = open('a.txt', 'wb')
f.__iter__()  # 文件

2.迭代器

2.1定义
含有__iter__() 与 ___next__()方法的就是迭代器。
可迭代对象.__iter__() --生成--> 迭代器

文件对象即是可迭代对象,也是迭代器对象。
迭代器是一种不依赖索引取值的方式。
f.__next__()  # 文件
2.2转迭代器
可迭代对象都可以转为迭代器.
str1 = '123'.__iter__()
list1 = [1, 2, 3].__iter__()
dic = {'k1': 'v1'}.__iter__()
tuple1 = (1, 2, 3).__iter__()
set1 = {1, 2, 3}.__iter__()

# 可迭代对象使用.__iter__() 方法后拥有了.__next__()方法
str1.__next__()
list1.__next__()
dic.__next__()
tuple1.__next__()
set1.__next__()
2.3迭代器取值
迭代器对象执行__next__方法取值,一次取一个元素。
迭代器取完值后在执行__next__方法取值就会报错 StopIteration (停止迭代)。
str1 = 'abc'.__iter__()  # 生成迭代器对象
print(str1.__next__())  # a 取第一个值
print(str1.__next__())  # b 取第二个值
print(str1.__next__())  # c 取第三个值

# 在取就报错 StopIteration
print(str1.__next__())  
2.4案例
每次str1.__iter__()都生成一个新的迭代器对象。
所以每次.__next__() 取的值都是同一个. 
str1 = 'abc'

print(str1.__iter__().__next__())  # a
print(str1.__iter__().__next__())  # a
print(str1.__iter__().__next__())  # a
print(str1.__iter__().__next__())  # a
2.5for循环
2.1原理
for循环原理:
1.后关键字in后面的数据先调用.__iter__()方法转为迭代器对象。
2.循环执行.__next__()方法取值。
3.取完之后再次执行.__next__()就会报错,但是for循环会自动捕获错误并处理。
2.2while练习
for 能做的 while都能做。
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
# 生成迭代器对象
list1 = l1.__iter__()

count = len(l1)
while count:
    print(list1.__next__())
    count -= 1
2.3递归练习
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
# 生成迭代器对象
list1 = l1.__iter__()

count = len(l1)


def func(count):
    if count == 0:
        return
    print(list1.__next__())
    return func(count - 1)


func(count)

3.异常捕获

3.1定义
1.异常代码运行出错会导致异常,异常发送后如果没有解决方案则直接结束程序。
2.异常三个组成部分
    2.1 traceback 代码错误所在的行
    2.2 xxxError  错误类型
    2.3 冒号       错误的详情原因,解决bug的关键
    
错误类型:
1.语法错误:不能出现,语法出错就立刻修复。
2.逻辑错误:可以被出现,逻辑就尽快修复。
	""" 修改逻辑错误的过程骑手就是从头到尾清理思路的过程 """
3.2语法结构
try:
	被检测的代码
except 错误类型 as e:
	被检测代码出错后的处理机制。 e变量名 接收错误信息。
try:
    name
except NameError as e:
    print(e, '结束程序运行!')  # name 'name' is not defined
    
Exception 是所有异常的父类,所有异常都可以检测到。
3.3for循环
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
list1 = l1.__iter__()
while True:
    try:
        print(list1.__next__())
    except Exception:
        break
不推荐使用迭代。
执行一次就有一句try语句。
try语句检测程序会占用额外的资源。
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55]
# 生成迭代器对象
list1 = l1.__iter__()


def func(list1):
    try:
        print(list1.__next__())
    except Exception:
        return
    return func(list1)


func(list1)

4.取值对比

迭代取值于索引取值对比:
迭代:不依赖于索引取值,通用取值方式。
	 顺序永远都是往下一个取值,无法重复获取。
索引:需要提供有序的容器类型,才能取值,不通用取值方式。
	可以重复取值

5.生成器

5.1定义
生成器 <==> 生成器对象
生成器就是自定义制作迭代器。
5.2定义生成器
关键字 yield 
生成器 ==> 迭代器
但函数体内含有yield关键字 那么在第一调用函数的时候,并不会执行函数代码,而是将函数变成生成器。
def my_ge():
	print('你好')
	yield  

my_ge()  # 不会执行my_ge函数内的内容
5.3获取值
一个yield对应一个__next__。
以yield为分割点分, 一个yield与它之前的代码为迭代器的一个元素。

执行一次.__next__()代码往下运行到yield 就停止,放回后面yield之前的数据。
再次执行一个.__next__()代码接着上次停止的地方继续往后, 在次遇到yield再停止。

如果yield 后面还有语句,而语句后面没有yield,这个语句就不会在执行。
def my_ge():
    print('你好1')
    yield
    print('你好2')
    yield
    print('你好3')
    yield
    print('你好4')
    yield
    print('你好5')  # 不会显示


res = my_ge()
res.__next__()
res.__next__()
res.__next__()
res.__next__()

image-20211122155304918

5.4实例
# 模仿range
def my_range(start_num, end_num=None, len_num=1):
    if not end_num:
        end_num = start_num
        start_num = 0

    while start_num < end_num:
        yield start_num
        start_num += len_num


for i in my_range(1, 10, 2):
    print(i)

6.yield关键字

6.1对比
yield
1. 可以设置返回值  yield 返回值 (支持多个值并组织成元组。)
	在阻塞在yiele 语句时, 会将yield后面的值放回给迭代器对象。
2. 函数体代码遇到  yield 不会在往后运行 进入阻塞态
3. 第三种传值的方式 yeied 可以将函数变成生成器,还可以接受传值。

return
1.可以有返回值 (支持多个值并组织成元组。)
2.函数体遇到return直接结束
6.2yield传值
yield 有对应的 .send方法进行传值。
格式:
迭代器对象.send(参数1,···)
函数中:
value=yield  
.send()方法 
不带任何参数(None表示)==> 等同于 next 方法
	在阻塞在yiele 语句时, 会将yiele 后面的值返回
带一个参数,先将值通过send传递给tield 在赋值给变量。
6.3不带参数
def my_ge():
    while True:
        yield 1


res = my_ge()
print(res.__next__())  # 1
print(res.__next__())  # 1
def my_ge():
    while True:
        yield 1  # 


res = my_ge()
print(res.send(None))  # 1
print(res.send(None))  # 1
6.4带参数
程序中第一次使用生成器调用 send() 函数时,不能使用带参数的 send() 函数。
def my_ge():
    while True:
        num = yield
        print(num)


res = my_ge()
print(res.send(1))  

"""
# TypeError: can't send non-None value to a just-started generator 报错
无法向刚启动的生成器发送非None值
"""
def my_ge():
    while True:
        num = yield
        print(num)


res = my_ge()

print(res.__next__())
res.send(1)
res.send(2)

7.生成器表达式

生成器表达式内部的代码只有在迭代值的时候才会执行。
迭代器节省空间,只有在我们取值的时候迭代器才会产生值。
d1 = (i for i in range(10))
# print(d1)  # generator 迭代器

for i in d1:
    print(i, end=' ')
# 调用之前是函数 调用之后是生成器
def test():
    for i in range(4):
        yield i


g = test()  # 初始化生成器对象 (0, 1, 2, 3)

for n in [1, 10]:
    g = (i + n for i in g)  # 第一次不执行

res = list(g)
print(res)  # 20 21 22 23
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值