zoukankan      html  css  js  c++  java
  • ql的python学习之路-day10

    前言:本节主要讲解迭代器和生成器

    迭代器&生成器

    一、生成器(generator)

    循环占用大部分的容量内存,如果只需要循环前面的几个结果那怎么样做呢,在python中有一种一边循环一边计算的机制,称为生成器:generator,就能解决这个问题。

    生成器只有在调用的时候才会产生相应的数据,用__next()__方法调用(2.7版本里是next()),生成器只能记录当前的位置,不能后退也不能记录以后的数据。

    实例:斐波那契数列中的生成器

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 # Author:qinjiaxi
     4 def fib(max):
     5     n, a, b = 0, 0, 1#初始化
     6     while n < max:#循环
     7         yield b
     8         #print(b)#打印b
     9         a, b = b, a + b
    10         n += 1
    11     return "done"#异常的时候打印的消息
    12 
    13 #抓异常
    14 g = fib(6)
    15 while True:
    16     try:
    17         x = next(g)
    18         print("g:", x)
    19     except StopIteration as e:
    20         print("Generator return value:", e.value)
    21         break
    22 #调用生成器
    23 f = fib(10)
    24 print(f)#打印生成器对象的内存地址
    25 print(next(f))#取第一个值
    26 print(f.__next__())#取第二个值
    27 print(next(f))#取第三个值
    28 print("---start loop---")
    29 for i in f:#循环取剩下的数据
    30     print(i)
    31 #注:当用next()方法调用次数超过设定值时,会产生异常
    32 
    33 #理解其中的a, b = b, a + b
    34 t = (b, a + b)#实际有个临时变量t,t是一个元组
    35 a = t[0]
    36 b = t[1]
    37 #t不会显式的出现在代码中

    生成器实际工作中的应用:协程(单线程并行处理),异步io处理

    yeild作用是保存当前状态并返回,无返回值(返回值是None)

    next方法调用yield,send方法调用yield并给yield传值,yield后面加上一个变量,可以返回变量

    协程1源码:

     1 #!/user/bin/env python
     2 #-*-coding:utf-8 -*-
     3 #Author: qinjiaxi
     4 import time
     5 def consumer(name):
     6     print("%s准备吃包子了" % name)
     7     while True:
     8         baozi = yield
     9         print("包子[%s]被%s吃了" % (baozi, name))
    10 
    11 c = consumer('ql')
    12 #next(c)#停在yield位置(中断,返回迭代值)
    13 #c.__next__()#从yield下一句开始执行,由于默认没有给yield传递参数所以返回的是None,执行完后又回到yield这一行
    14 #c.__next__()
    15 #c.send("韭菜馅的")#send方法可以调用yield并且给yield传送参数
    16 
    17 def producer(name):
    18     c = consumer('A')
    19     c1 = consumer('B')
    20     c.__next__()#A准备吃包子了
    21     c1.__next__()#B准备吃包子了
    22     print("%s开始做包子了" % name)
    23     for i in range(10):
    24         time.sleep(1)
    25         print("做了一个包子分两半")
    26         c.send(i)#传递i到A的yield,给A的yield赋值
    27         c1.send(i)#传递i到B的yield,给B的yield赋值
    28 producer('qinlang')

    协程2源码:

     1 #!/user/bin/env python
     2 #-*-coding:utf-8 -*-
     3 #Author: qinjiaxi
     4 import time
     5 def consumer():
     6     r = ''#初始化r
     7     while True:
     8         n = yield r#将值传给n,然后执行下一句,碰到r变量再返还r给producer函数
     9         print("[consumer] is consuming %s " % n)
    10         r = '200 is ok'
    11 
    12 def producer(c):
    13     c.send(None)
    14     n = 0
    15     while n < 5:
    16         n = n + 1
    17         time.sleep(1)
    18         print("[producer] is producting %s" % n)
    19         r = c.send(n)
    20         print("[producer] cousumer return %s" % r)
    21     # for i in range(6):
    22     #     time.sleep(1)
    23     #     print('[producer] is producing %s ' % i)
    24     #     r = c.send(i)#传n值给生成器yield
    25     #     print('[producer] consumer return %s' % r)
    26     c.close()#关闭生成器
    27 c = consumer()
    28 producer(c)

    结论:

    一个带有yield的函数就是一个generator,它和普通函数不一样,生成器generator看起来像函数,其实不会执行任何函数代码,直到对其调用next()方法(在for循环中会自动调用next()方法)才会执行。虽然执行仍然像函数一样执行,其实当执行到yield时候就会中断并返回一个迭代值,当再次调用next()方法时从yield下一句开始执行。看起来就好像一个函数在正常执行的时候被yield中断了数次,每次中断都会通过yield返回迭代值。

    二、迭代器(Iterator)

    我们知道能直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如list、tuple、str、set、dict

    一类是generator,包括生成器和带yield的generator function

    *这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

    可以使用isinstance()来判断一个对象是否是iterable对象:

     1 >>> from collections import Iterable
     2 >>> isinstance([], Iterable)
     3 True
     4 >>> isinstance('abc', Iterable)
     5 True
     6 >>> isinstance({}, Iterable)
     7 True
     8 >>> isinstance((i for i in range(10)), Iterable)
     9 True
    10 >>> isinstance(100, Iterable)
    11 False

    生成器不但可以作用于for循环,还可以被next()函数不断的调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值。

    *可以被next()函数调用并且不断返回下一个值的对象称为迭代器:Iterator

    可以使用isinstance()来判断一个对象是否是interator对象:

    1 >>> from collections import Iterator
    2 >>> isinstance([], Iterator)
    3 False
    4 >>> isinstance({}, Iterator)
    5 False
    6 >>> isinstance((i for i in range(10)), Iterator)
    7 True

    由上可知生成器都是Iterator对象,但list、dict、str虽然是Iterable但不是Iterator。

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    1 >>> isinstance(iter([]), Iterator)
    2 True
    3 >>> isinstance(iter('abc'), Iterator)
    4 True
    5 >>> isinstance(iter({}), Iterator)
    6 True

     为什么list、dict、str等数据不是Iterator?

    因为python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断的返回下一个数据,直到没有数据时抛出StopIteration错误。可以把数据流看做是一个有序序列,但是我们提前并不知道序列的长度,只有通过next()函数按需继续下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据的时候才计算。

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。但是list是永远不可能村粗全体自然数的。

    小结:

    凡是可作用于for循环的对象都是Iterable类型(可迭代类型);

    凡是可以作用于next()函数的对象都是Iterator类型(迭代器类型),它们表示一个惰性的计算序列;

    集合数据类型例如list、dict、str等都是Iterable(可迭代对象)但不是Iterator(迭代器),可以通过iter()函数获得一个迭代对象。

    Python的for循环的本质就是不断的通过调用next()函数实现的。

    python3.0中range(10)其实是一个迭代器

    python2.x中xrange(10)是迭代器

  • 相关阅读:
    JS判断是否是IE浏览器
    JS在页面光标位置插入新内容
    JS限制文本框输入金额,保留2位小数
    JS格式化日期方法
    MySQL数据库中16进制进行位运算
    Java过滤特殊字符的正则表达式
    JAVA查看线程信息
    win7 安装Redis
    JAVA获取catch中的异常信息
    cpu占用率过高debug
  • 原文地址:https://www.cnblogs.com/qinlangsky/p/9551666.html
Copyright © 2011-2022 走看看