zoukankan      html  css  js  c++  java
  • 迭代器和生成器

    迭代器

    • 思维打开:如何从列表中取值
      • 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]
        []
  • 相关阅读:
    自定义TAB
    android Tabhost部件
    PHP链接MYSQL
    Server.Transfer传值方法的使用
    JQuery一些简单常用的方法
    经典mssql语句大全
    C#中如何将DataTable中的数据写入Excel
    03、JavaEECookie & Session
    08、C# Task的使用
    06、JavaEEJSP基本语法
  • 原文地址:https://www.cnblogs.com/liliudong/p/9572745.html
Copyright © 2011-2022 走看看