迭代器、for循环、生成器

1.迭代器协议:
        对象必须提供一个__next__()方法,执行该方法要么返回迭代中的下一项,要么就捕捉一个StopIteration异常,已终止迭代(只能往后走不能往前退)

2.可迭代对象:
      实现迭代协议对象(如何实现:对象内部定义一个__iter__()方法)

3.协议是一种约定,可迭代对象实现了迭代器协议,python内部工具(如for循环,sum、min、max函数等)使用迭代器协议访问对象

for循环机制
      for循环的本质:循环所有对象,全都是使用迭代器协议

1.迭代器
对于序列类型:字符串、列表、元组,我们可以通过索引的方式迭代取出其包含的元素。但对于非序列类型:字典、集合、文件是没有索引的,若想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

2.可迭代对象
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
open('a').__iter__

3.迭代器对象
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
   open('a').__iter__()
   open('a').__next__()

注:
    迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

迭代器对象 

l = [1, 2, 3]
# 一:下标访问方式
print(l[0])
print(l[1])
print(l[2])
print(l[3])  # 列表超出索引边界:IndexError
'''
结果
1
2
3
IndexError: list index out of range
'''

# 使用迭代器协议访问,不需要依赖索引迭代取值了
iter_l = l.__iter__() # 遵循迭代器协议,生成可迭代对象就是迭代器对象
while True:
    try:
        k = next(iter_l) # 可迭代器对象调用__iter__中的内置__next__方法
        print(k)
    except StopIteration: # 抛出StopIteration异常,并终止迭代
        break
'''
结果:
1
2
3
'''

# 二:遵循迭代器协议访问方式,迭代器允许惰性求值,只有在请求下一个元素时迭代器对象才会去生成它
x = 'Alex'
iter_x = x.__iter__()  # 得到迭代器对象,可迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
print(iter_x.__iter__()is iter_x) # True
# 打印字符串迭代器对象地址
print(iter_x)
# <str_iterator object at 0x10a5c2e80>
print(iter_x.__next__())  # 迭代器对象调用__iter__中的内置__next__方法,等同于next(iter_x),next()是系统内置函数
print(iter_x.__next__())  # 每次打印一个字符串的字符
print(iter_x.__next__())
print(iter_x.__next__())
print(iter_x.__next__())  # 捕捉StopIteration异常,终止迭代
'''
结果:
A
l
e
x
StopIteration
'''

for循环

dit = {'name': '3+2', 'age': 18, 'sex': '无'}
for i in dit:
    print(i)
'''
for循环工作原理:
1.执行in后面的对象dit.__iter__()方法,得到一个迭代器对象iter_dit
2.执行next(iter_dit),将得到的值赋值给i,然后执行循环体代码
3.依次循环,直到捕捉StopIteration异常,终止循环
'''

注:for循环是遵循迭代器协议去执行操作,与索引没有任何关系 

为什么要用for循环

# 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
l = [1, 2, 3] 
num = 0
while num < len(l): # 迭代
    print(l[num])
    num += 1

序列类型:字符串、列表、元组都有下标,是可以通过下标取值,但是非序列类型:字典、集合、文件对象,是不能通过下标取值的,for循环是基于迭代器协议提供一个统一遍历所有对象的方法,但遍历对象之前,对象调用__iter__()方法将生成一个可迭代器对象,然后迭代器协议去实现循环访问,所有的对象都可以通过for循环来遍历

迭代器的优缺点

优点:
     提供一种统一的、不依赖于索引的迭代方式
     惰性计算,节省内存
缺点:
     无法获取长度(只有在next完毕才知道到底有几个值)
     一次性的,只能往后走,不能往前退

生成器 

生成器
    一种数据类型,自动实现迭代器协议,对象只需要调用内置__next__()方法(其他的数据类型需要调用自己内置的方法__iter__()方法,生成器就是可迭代对象

生成器分类即python中的表现形式:(Python有两种不同的方式提供生成器)

    1.生成器函数:常规函数定义,但是,yield关键字代替了return语句返回结果,yield关键字一次返回一个结果,在每个结果中间,保留函数的状态,以便下次从它离开的地方继续执行

    2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

  • 语法上和函数类似:生成器函数和常规函数几乎是一样的,他们都是使用def语句进行定义,差别在于,生成器使用yield关键字可以返回多个值,而常规函数使用return语句只能返回一个值
  • 自动实现迭代器协议:对于生成 器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环、sum函数)由于生成器自动实现了迭代器协议,所以,我们可以调用他的next()方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
  • 状态挂起:生成器使用yield关键字返回一个值,yield关键字保留生成器函数的状态,以便之后从它离开的地方继续执行 

生成器函数 

'''
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码,需要生成器对象调用__next__()/next()方法,才可以执行函数内部代码
1、把函数做成迭代器
2、对比return,可以返回多次值,可以挂起/保存函数的运行状态
'''

def func():
    stay = yield '优乐美'
    print('停留的位置', stay) 
    yield '香飘飘'

# gen_res是一个生成器对象
gen_res = func()
# 打印生成器对象地址
print(gen_res)
# <generator object test at 0x10d7e8408>
print(gen_res.__next__())  # 生成器对象调用__next__()/next()方法,保留函数状态
print(next(gen_res))  # 从上一次保留函数状态地方,继续往下执行操作
print(gen_res.send('stay')) # 表达形式,yield 1i相当于return控制的是函数的返回值,stay = yield的另外一个特性,接受send传过来的值,赋值给stay,send传入的值和yield返回的值没有任何关系
'''
结果:
    优乐美
    香飘飘
'''

三元表达式 

coffemilk = '优乐美'
coffemilk = 'Tea'
res = '喜之郎' if coffemilk == '优乐美' else '茶'
print(res)

列表解析(列表推导式)

list_res = ['蘑菇%s' % i for i in range(1, 11) if i <= 6] # 内存占用大,容易卡死
print(list_res)
'''
结果:
['蘑菇1', '蘑菇2', '蘑菇3', '蘑菇4', '蘑菇5', '蘑菇6']
'''
错误示例:
list_res = ['蘑菇%s' % i for i in range(1, 11) else '采蘑菇的小姑娘'] # 在列表解析中不能使用else语句,会标红

 生成器表达式

# 生成器表达式自动实现迭代器协议,迭代器协议对象调用__next__()/next()方法
murphy = ('土豆%s' % i for i in range(1, 11)) # 几乎不占内存 
# 打印生成器对象地址
print(murphy)
# <generator object <genexpr> at 0x104502408>
print(murphy.__next__())  # 生成器对象调用__next__()方法
print(next(murphy))  # next()内置函数,next()本质调用__next__(),
print(next(murphy))  
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
print(next(murphy))
# print(next(murphy))  # 捕捉StopIteration异常,终止迭代
'''
结果:
    土豆1
    土豆2
    土豆3
    土豆4
    土豆5
    土豆6
    土豆7
    土豆8
    土豆9
    土豆10
    StopIteration
'''

注:生成器只能遍历一次 

1.把列表解析[]换成()得到的生成器表达式
2.列表解析与生成器表达式都是一种遍历的编程方式,生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如,sum函数....是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议

print(sum(i for i in range(10)))

 总结

  1. 生成器的好处是延迟计算,一次返回一个结果,它不会一次生成所有的结果,这对于大数据量处理,会非常有用
  2. 生成器还能有效提高代码可读性
  3. 生成器允许你以一种非常pythonic的方式来创建迭代器。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值