迭代器和生成器

迭代器

  • 思维打开:如何从列表中取值
    • for循环
    • 索引
  • 迭代器应用在可for循环的数据中,即拥有__iter__方法
    • 字符串
    • 集合
    • 列表
    • 元组
    • range()
    • f = open()
    • enumerate 枚举
    • 字典
  • 可迭代协议
    • 只要内部含有__iter__方法都是可迭代的

        	print(__iter__() in dir([]))
      
  • 迭代器协议
    • 内部含有__next__和__iter__方法都是迭代器
  • isinstance(数据, 类型) 判断数据是否是类型
  • 迭代器协议和可迭代协议
  • 只要能被for循环的都是可迭代的
  • 可迭代的.__iter__()就是一个迭代器
  • 迭代器中的__next__()方法就是一个一个的获取值

迭代器的优点

  • 迭代器可以自定取元素

  • 节省内存空间

      	#模仿for循环获取列表的元素
      	l = [1, 2, 3, 4, 5]
      	iterator = l.__iter__()
      	while 1:
      		try:
      			print(iterator.__next__())
      		except:
      			break
    

实际例子

  • 实际告诉你这是个迭代器
  • 可迭代对象
  • 直接给你内存地址

用for的情况

  • 只有内部有__iter__()方法的数据才能用for循环

生成器 -一个迭代器

  • 本质也是迭代器
  • 生成器可以记住自己已经迭代到哪儿了
  • 迭代器不能记住自己迭代到哪儿了

生成器函数

  • 本质上就是我们自己写的函数

  • 只要含有yield就是生成器函数

  • 函数yieldreturn不能共用

  • yield只能在函数里用

  • 生成器函数执行之后会产生一个生成器作为返回值

      	#普通函数
      	def func():
      		print(1)
      		return 'a'
      	#调用
      	ret = func()
      	print(ret)
      	#结果
      	1
      	a
      	
      	#生成器函数
      	def generator():
      		print(1)
      		yield 'a'
      	#调用
      	ret = generator()
      	print(ret)
      	#结果
      	<generator object generator at 0x000000000>	#返回的是一个内存地址
      	#换种方式调用
      	ret = generator()
      	print(ret.__next__())
      	#结果
      	1
      	a
    
  • yield不会像return一样结束函数

  • yield也会返回一个值

  • 每调用一个__next__()就会输出一个yield返回的值

      	#定义一个生成器函数
      	def generator():
          for i in range(100):
              yield i
      	
      	g1 = generator()	
      	g2 = generator()
      	
      	#循环打印生成器函数内容
      	while 1:
      	    try:
      	        print(g1.__next__())
      	        print(g2.__next__())
      	    except:
      	        break
         #结果是同步运行,所以相当于双线程  
    

生成器函数进阶

  • yield后不再有函数体了

      	def wrapper():
      		print(1)
      		yield 8
      		print(2)
      		yield 8
      		print(2)	#后方没有yield可以打印但是报错
    
  • __next__()send效果可以一致

    • 获取下一个值的效果一致
  • 但是也有不同,send()能够传值

    • 只不过获取下一个值的时候给上一个yield传一个值
  • send使用注意事项

    1. 第一个值必须用__next__()调取

    2. 最后一个yield不能接收外部的值

       #send()不传入参数
       def wrapper():
       	print('=')
       	yield '第一个'
       	print('==')
       	yield '第二个'
       #调用
       g = wrapper()
       print(g.__next__())
       print(g.send(None))	
       #结果
       =
       第一个
       ==
       第二个
       
       #send传入任意参数,如'hello'
       def wrapper():
       	print('=')
       	countent = yield '第一个'	#这里给yield赋值countent
       	print('~~', countent)	#这里添加一个打印信息
       	print('==')
       	yield '第二个'
       #调用
       g = wrapper()
       print(g.__next__())	#第一个值的调取必须用__next__()
       print(g.send(hello))	
       #结果
       =
       第一个
       ~~ hello	#这里结果出现了hello说明参数传进去了
       ==
       第二个
      

练习应用

  • 一个计算战绩平均值的应用

      	#生成器函数,用来计算战绩平均值
      	def averge():
      	    count = 0
      	    aver = 0
      	    num = 0
      	    add = 0
      	    while 1:
      	        num = yield
      	        add += num
      	        count += 1
      	        aver = add/count
      	        yield aver
      	
      	#持续输入的函数,用来输入每局成绩
      	def print_num():
      	    g = averge()
      	    while 1:
      	        num = int(input('输入:'))
      	        g.__next__()
      	        print(g.send(num))
      	
      	print_num()
    
  • 将上面的作业改成装饰器形式

    • 预激活装饰器

        #装饰器作为持续输入的函数
        #装饰器为生成器函数提供用户输入
        def print_num(func):
            def inner(*args, **kwargs):
                g = func(*args, **kwargs)
                while 1:
                    num = int(input('输入:'))
                    g.__next__()
                    print(g.send(num))
                return g
            return inner
        
        #生成器函数
        @print_num
        def averge():
            count = 0
            aver = 0
            num = 0
            add = 0
            while 1:
                num = yield
                add += num
                count += 1
                aver = add/count
                yield aver
        #调用
        averge()	
      
  • yield from 用法

      	def generator():
      		a = '12345'
      		b = 'abcde'
      		yield from a
      		yield from b
      	#调用
      	g = generator()
      	for i in g:
      		print(i)
      	#结果
      	1
      	2
      	3
      	4
      	5
      	a
      	b
      	c
      	d
      	e
    

生成器表达式

列表推导式

list = [i for i in rang(10)]
#得到的结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

生成器表达式

g = (i for i in range(10))	#生成器
print(g)
#结果
<generator object <xxx> at 0x0000000>	#一个内存地址
#利用for循环获取生成器里的元素
for i in g:
	print(i)
#结果
0
1
2
3
4
5
6
7
8
9
  • 生成器表达式和列表推倒式区别
    • 返回值不一样
    • 括号不一样
    • 二者优缺点
      • 列表推倒式代码量少,观看直观,但是占内存
      • 生成器表达式几乎不占用内存但是使用起来代码量多,观看不直观

经典面试题

  • 生成器是一个有指针的数据类型,因此循环一遍之后指针在数据之后,无法再继续取值

      #定义一个生成器函数
      def generator():
      	for i in range(6)
      		yield i
      
      g = generator()	#生成器
      g1 = (i for i in g)
      g2 = (i for i in g1)
      
      print(list(g1))
      print(list(g2))
      #结果
      [0, 1, 2, 3, 4, 5]
      []
原文地址:https://www.cnblogs.com/liliudong/p/9572745.html