Python Iterator and Generator

Iterator

迭代器(Iterator)和可迭代对象(Iterable)往往是绑定的。可迭代对象就是我们平时经常用的list ,string, tuple这种。事实上迭代器的概念会比可迭代对象宽泛很多,一会举几个例子就能明白。

​ 在使用list这种数据类型的时候,我们经常会使用下面这种迭代方式:

  1. # eg 1
  2. mylist = [1,2,3,4]
  3. for x in mylist:
  4. print(x)
  5. >>>1
  6. >>>2
  7. >>>3
  8. >>>4

​ 有时候会很奇怪for循环为什么可以这么用,列表的索引竟然会自动往后一个一个走,走到结尾了,还会自动停下来,不会有list out of range的报错。神奇。其实for循环做的事情不止这么简单。要说明这个问题,得先说迭代器具体怎么做,是什么。

​ 要创建一个迭代器,就必须要实现两个方法,分别是__iter__()__next__(),以及实现异常机制StopIteration。请看下面的例子:

  1. # eg 2
  2. class PowTwo:
  3. """Class to implement an iterator of powers of two"""
  4. def __init__(self, max = 0):
  5. self.max = max
  6. def __iter__(self):
  7. self.n = 0
  8. return self
  9. def __next__(self):
  10. if self.n <= self.max:
  11. result = 2 ** self.n
  12. self.n += 1
  13. return result
  14. else:
  15. raise StopIteration

​ 可以看到,迭代器是通过写一个类来定义的,类里面实现了刚刚所说的__iter__()__next__()方法。这个迭代器是用来产生一系列2的指数次方的数字的,具体用法看下面:

  1. a = PowTwo(4)
  2. i = iter(a) # attention please
  3. next(i)
  4. >>>1
  5. next(i)
  6. >>>2
  7. next(i)
  8. >>>4
  9. next(i)
  10. >>>8
  11. next(i)
  12. >>>16
  13. next(i)
  14. >>>Traceback (most recent call last):
  15. ...
  16. StopIteration

​ 仔细看哦,第一行代码用4创建了一个实例a,设置了这个迭代器的迭代上限,然后并不是直接用这个实例就可以了,还得调用iter()函数去把a彻底进化成一个Iterator,进而有了接下来next()函数的闪亮登场。其实我也蛮奇怪,直接用这个类不好么,比如下面的代码:

  1. a = PowTwo(4)
  2. a.__iter__()
  3. a.__next__()
  4. >>>1
  5. a.__next__()
  6. >>>2
  7. a.__next__()
  8. >>>4
  9. a.__next__()
  10. >>>8
  11. a.__next__()
  12. >>>16
  13. next(i)
  14. >>>Traceback (most recent call last):
  15. ...
  16. StopIteration

​ 完全没问题,但是你自己比较一下二者的代码,哪个美一点毋庸置疑。。。再说了,跟装饰器一样,一切为了简洁优美而生嘛。

​ OK,可以回到最初的起点——for循环了。示例1中的代码,for循环到底做了什么呢,答案是,for循环其实是先把mylist变成迭代器,然后用while循环去迭代:

  1. iter_obj = iter(mylist)
  2. while True:
  3. try:
  4. element = next(iter_obj)
  5. except StopIteration:
  6. break

​ 这样一路解释过来,应该就不难理解迭代器了。总结一下就是:

  1. 如何已经有一个可迭代对象,那么直接用iter()函数去初始化它,然后疯狂next()即可;
  2. 如果没有现成的可迭代对象,那就自己写一个类,类里面记得实现__iter__()__next__()方法,以及异常机制StopIteration,然后操作同1;
  3. 如果你想要一个无限迭代器,不要实现异常机制StopIteration即可。



Generator

​ 这玩意儿就比迭代器复杂点了,所以还得分几个小点,逐个击破。

1. 生成器是啥

​ 生成器也是Python中面向一系列需要迭代的问题,常用的解决方案。既然有了迭代器,可以解决很多迭代的问题,那为啥子还要生成器勒?

​ 主要的原因是迭代器的开销太大了。对于一些小问题还好,大的问题需要迭代的元素很庞大的时候,迭代器就使不上劲儿了。而且,创建一个迭代器,说实话也还挺麻烦的,看看上面的小总结的第二点,你得实现这些方法和手动处理异常。

​ 而且迭代器要写类,其实一个函数可以搞定的事情,何必那么复杂。正是应了这个景,生成器也就是在函数对象上搞事情的。

​ 这个原因点到即止,先把生成器讲清楚了,自然就通透了。先看一个小例子,写一个生成器函数(Generator Function):

  1. # eg 1
  2. def my_gen():
  3. n = 1
  4. print('This is printed first')
  5. yield n
  6. n += 1
  7. print('This is printed second')
  8. yield n
  9. n += 1
  10. print('This is printed at last')
  11. yield n

​ 上面就是一个简单的生成器函数,多了一种关键字yield,细心看会发现这个函数竟然没有return!再细看代码,没错,yield替代了return。那怎么用呢,有两种用法如下:

  1. # usage 1
  2. a = my_gen()
  3. next(a)
  4. >>>This is printed first
  5. 1
  6. next(a)
  7. >>>This is printed second
  8. 2
  9. next(a)
  10. >>>This is printed at last
  11. 3
  12. next(a)
  13. >>>Traceback (most recent call last):
  14. ...
  15. StopIteration
  1. # usage 2
  2. for item in my_gen():
  3. print(item)
  4. >>>
  5. This is printed first
  6. 1
  7. This is printed second
  8. 2
  9. This is printed at last
  10. 3

​ 对于用法1,把函数赋值给了a,然乎疯狂next()即可。你会发现,我们并没有像迭代器那样实现__iter__()__next__()方法,以及异常机制StopIteration,只是用了一个yield关键字,这个生成器函数却达到了迭代器一样的效果。

​ 对于用法2,更牛皮了,甚至不用赋值的操作,直接for这个生成器函数。。。

2. 循环机制生成器

​ 刚刚那个小函数,就是一个最普通的例子,那问题是如果有多个n想要玩,岂不是来多少手动写多少?那当然还有循环的玩法,带有循环机制的生成器。下面是一个逆序输出的小例子:

  1. # eg 2
  2. def rev_str(my_str):
  3. length = len(my_str)
  4. for i in range(length - 1,-1,-1):
  5. yield my_str[i]
  6. for char in rev_str("hello"):
  7. print(char)
  8. >>>o
  9. >>>l
  10. >>>l
  11. >>>e
  12. >>>h

​ 没错,真无聊,犯得着逆序输出的程序还得上生成器么,犯不着,但是,只是想给出这个循环机制生成器的概念。如果你发现这个rev_str()函数和普通的逆序函数完全一样,只是return换成了yield,那就万事大吉,要理解的就是这个。就这样一记简单的替换操作,你就得到了一个生成器,是不是比迭代器省事儿多了。

3. 生成器表达式(Generator Expression)

​ 不知道你有没有用过列表生成式,没用过也应该看到过,这类似于匿名函数,语法简洁,比如:

  1. # eg 3
  2. my_list = [1, 3, 6, 10]
  3. [x**2 for x in my_list]
  4. >>>[1, 9, 36, 100]

​ 生成器表达式和这个几乎一样,不信你看:

  1. # eg 4
  2. my_list = [1, 3, 6, 10]
  3. a = (x**2 for x in my_list)
  4. next(a)
  5. >>>1
  6. next(a)
  7. >>>9
  8. next(a)
  9. >>>36
  10. next(a)
  11. >>>100

​ 把列表生成式的[]直接改成(),就得到了一个生成器。

4. Why Generator???

(1). 简洁

​ 回到最开始迭代器的那个类的例子,用生成器咋写呢?

  1. def PowTwoGen(max = 0):
  2. n = 0
  3. while n < max:
  4. yield 2 ** n
  5. n += 1

​ 简洁吧,然后你就可以用这个生成器函数遨游了。

(2). 开销小

​ 同样的一个需要迭代的功能,如果用普通函数写,一旦需要迭代的元素特别多,在使用的时候,普通函数需要等所有的元素计算出来了,然后把返回值给你。生成器就不是了,它一次计算出一个,用的时候就取一个,并且它还会记住位置,下次用就计算下一个,这样对空间的开销也是很小的。

(3). 无限

​ 看下面的函数:

  1. def all_even():
  2. n = 0
  3. while True:
  4. yield n
  5. n += 2

​ 写普通函数,你必然做不到写出一个可以无限操作的函数,生成器却可以。(迭代器也可以,就是麻烦点儿)

5. 再给一个例子

  1. # 利用yield生成一个斐波那契数列的生成器
  2. def fib(max):
  3. n,a,b=0,0,1
  4. while n<max:
  5. yield b
  6. a,b=b,a+b
  7. n+=1
  8. return 'done' # 要不要这句话都行
  9. f=fib(6)
  10. next(f) # 疯狂next()它
  11. >>>
  12. 1
  13. 1
  14. 2
  15. 3
  16. 5
  17. 8
  18. Traceback (most recent call last):
  19. ...
  20. StopIteration: done

Python Iterator and Generator的更多相关文章

  1. [Python学习]Iterator 和 Generator的学习心得

    [Python学习]Iterator 和 Generator的学习心得 Iterator是迭代器的意思,它的作用是一次产生一个数据项,直到没有为止.这样在 for 循环中就可以对它进行循环处理了.那么 ...

  2. Python3 Iterator and Generator

    Python3 Iterator and Generator iterator  主要是利用 iter 函数 >>> list=[1,2,3,4] >>> it = ...

  3. babel 不能统编译Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise的问题

    Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator.Generator.Set.Maps.Proxy.Reflect.Symbol.Promis ...

  4. Python生成器(generator)和迭代器(Iterator)

    列表生成式 a = [i+1 for i in range(10)] print(a) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 这就是列表生成式 生成器(generator) ...

  5. Python之生成器(generator)和迭代器(Iterator)

    generator 生成器generator:一边循环一边计算的机制. 生成器是一个特殊的程序,可以被用于控制循环的迭代行为.python中的生成器是迭代器的一种,使用yield返回值函数,每次调用y ...

  6. python中的generator, iterator, iterabel

    先来看看如果遇到一个对象,如何判断其是否是这三种类型: from types import GeneratorType from collectiuons import Iterable, Itera ...

  7. Python中生成器generator和迭代器Iterator的使用方法

    一.生成器 1. 生成器的定义 把所需要值得计算方法储存起来,不会先直接生成数值,而是等到什么时候使用什么时候生成,每次生成一个,减少计算机占用内存空间 2. 生成器的创建方式 第一种只要把一个列表生 ...

  8. python中的generator(coroutine)浅析和应用

    背景知识: 在Python中一个function要运行起来,它在python VM中需要三个东西. PyCodeObject,这个保存了函数的代码 PyFunctionObject,这个代表一个虚拟机 ...

  9. (转)Python中的generator详解

    本文转自:http://www.cnblogs.com/xybaby/p/6322376.html 作者:xybaby 注:本文在原文基础上做了一点点修改,仅仅作为个人理解与记忆,建议直接查看原文. ...

随机推荐

  1. .Net 通过设置Access-Control-Allow-Origin来实现跨域访问

    目录 # 前言 # 为每个API接口单独添加响应头 1.针对 ASP.NET MVC 项目的Controllers 2.针对 ASP.NET Web API项目的Controllers 3.针对ASP ...

  2. TypeScript算法与数据结构-队列和循环队列

    本文涉及的源码,均在我的github.有两部分队列和循环队列.有问题的可以提个issue,看到后第一时间回复 1. 队列(Queue) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...

  3. Flags Over Objects

    The Flags Over Objects anti-pattern occurs when behavior is written outside of an object by inspecti ...

  4. dapper支持DataSet

    在源代码中添加 /// <summary> /// describe:支持 DataSet /// </summary> /// <param name="cn ...

  5. WordPress教程之如何创建博客内容

    上两篇教程的链接: Wordpress教程之初识WordPress Wordpress教程之如何入门WordPress Hostwinds共享主机vps限时五折优惠链接 现在,你的 WordPress ...

  6. java中几个常见的问题

    1.正确使用equals方法 Object的equals方法容易抛出空指针异常,应使用常量或确定有值的对象来调用equals方法 例如: //不能使用一个值为null的引用类型变量来调用非静态方法,否 ...

  7. leetcode的Hot100系列--3. 无重复字符的最长子串--滑动窗口

    可以先想下这两个问题: 1.怎样使用滑动窗口? 2.如何快速的解决字符查重问题? 滑动窗口 可以想象一下有两个指针,一个叫begin,一个叫now 这两个指针就指定了当前正在比较无重复的字符串,当再往 ...

  8. SpringCloud解析之Zuul(二)

    本文基于Spring Cloud Edgware.SR6,Zuul版本1.3.1,解析Zuul的请求拦截机制,让大家对Zuul的原理有个大概的认识和了解.如有不对的地方,欢迎指正. 在上一期的Spri ...

  9. Excel催化剂100+大主题功能梳理导读

    Excel催化剂历经1年4个月的开发时间,终于荣登100+个大主题功能,完成数据领域的功能大矩阵,可以说在日常的数据处理及分析上,绝大部分的共性场景已经囊括其中,是数据工作者难得一遇的优秀作品之一.因 ...

  10. [剑指offer] 10. 旋转数组的最小数字

    题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 思路: 利用dp[i]保存盖2*i的矩形有多少种办法. 通过 ...