python生成器与迭代器详解


Posted in Python onJanuary 01, 2019

列表生成式:

例一:

a = [i+1 for i in range(10)]
print(a)

输出:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

例二:

L = [1, 2, 3, 4, 5]
print([i*i for i in L if i>3])

输出:

[16, 25]

例三:

L = [1, 2, 3, 4, 5]
I = [6, 7, 8, 9, 10]
print([i*a for i in L for a in I if i > 2 if a < 8])

输出:

[18, 21, 24, 28, 30, 35]

生成器:

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

示例:

L = [1, 2, 3, 4, 5]
I = [6, 7, 8, 9, 10]
g = (i*a for i in L for a in I )
print(g)

输出:

<generator object <genexpr> at 0x00000276586C1F48>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,可以通过generator的next()方法

next(g)

例一:

L = [1, 2, 3, 4, 5]
I = [6, 7, 8, 9, 10]
g = (i*a for i in L for a in I )
print(next(g))
print(next(g))
print(next(g))

输出:

6
7
8

例二:

L = [1, 2, 3, 4, 5]
I = [6, 7, 8, 9, 10]
g = (i*a for i in L for a in I if i > 2 if a < 8)
print(next(g))
print(next(g))
print(next(g))

输出:

18
21
24

因为generator保存的是算法,每次调用next(g)就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。正确的方法是使用for循环,因为generator也是可迭代对象:

例三:

g = (i*i for i in range(0, 5))
for i in g:
    print(i)

当我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n = n + 1

上面的函数可以输出斐波那契数列的前N个数:

>>> fib(6)
1
1
2
3
5
8

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
  n,a,b = 0,0,1

  while n < max:
    #print(b)
    yield b
    a,b = b,a+b

    n += 1

  return 'done'

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
print(fib(5))

输出:

<generator object fib at 0x0000023DC66C1F48>

调用方法:   ##但是用for循环调用generator时,\
            ##发现拿不到generator的return语句\
            ##的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

for i in fib(5):
    print(i)

输出:

1
1
2
3
5

或者:

date = fib(5)
print(date.__next__())
print(date.__next__())
print(date.__next__())
print('test')
print(date.__next__())
print(date.__next__())

输出:

1
1
2
test
3
5

send方法有一个参数,该参数指定的是上一次被挂起的yield语句的返回值

还可通过yield实现在单线程的情况下实现并发运算的效果

#_*_coding:utf-8_*_
__author__ = 'Alex Li'

import time
def consumer(name):
  print("%s 准备吃包子啦!" %name)
  while True:
    baozi = yield

    print("包子[%s]来了,被[%s]吃了!" %(baozi,name))


def producer(name):
  c = consumer('A')
  c2 = consumer('B')
  c.__next__()
  c2.__next__()
  print("老子开始准备做包子啦!")
  for i in range(10):
    time.sleep(1)
    print("做了2个包子!")
    c.send(i)
    c2.send(i)

producer("alex")

通过生成器实现协程并行运算

迭代器:

可以直接作用于for循环的数据类型有以下几种:

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

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

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

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

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

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

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

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

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

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

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

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

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

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结:

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

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

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

Python3的for循环本质上就是通过不断调用next()函数实现的,例如:

for x in [1, 2, 3, 4, 5]:
    pass

实际上完全等价于:

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

Python 相关文章推荐
python利用beautifulSoup实现爬虫
Sep 29 Python
python网络编程之读取网站根目录实例
Sep 30 Python
Python中使用partial改变方法默认参数实例
Apr 28 Python
由浅入深讲解python中的yield与generator
Apr 05 Python
Python 的类、继承和多态详解
Jul 16 Python
Python Pandas找到缺失值的位置方法
Apr 12 Python
Python实现的简单读写csv文件操作示例
Jul 12 Python
python实现梯度下降法
Mar 24 Python
Django 解决distinct无法去除重复数据的问题
May 20 Python
Python计算信息熵实例
Jun 18 Python
python获取linux系统信息的三种方法
Oct 14 Python
浅析python字符串前加r、f、u、l 的区别
Jan 24 Python
使用python3实现操作串口详解
Jan 01 #Python
python实现生成字符串大小写字母和数字的各种组合
Jan 01 #Python
python 内置模块详解
Jan 01 #Python
python配置grpc环境
Jan 01 #Python
python制作mysql数据迁移脚本
Jan 01 #Python
在python中将字符串转为json对象并取值的方法
Dec 31 #Python
对python中Json与object转化的方法详解
Dec 31 #Python
You might like
php Sql Server连接失败问题及解决办法
2009/08/07 PHP
关于PHP模板Smarty的初级使用方法以及心得分享
2013/06/21 PHP
解析php取整的几种方式
2013/06/25 PHP
php下获取http状态的实现代码
2014/05/09 PHP
thinkphp实现上一篇与下一篇的方法
2014/12/08 PHP
PHP 命名空间和自动加载原理与用法实例分析
2020/04/29 PHP
js宝典学习笔记(上)
2007/01/10 Javascript
基于jquery实现拆分姓名的方法(纯JS版)
2013/05/08 Javascript
用JavaScript实现一个代码简洁、逻辑不复杂的多级树
2014/05/23 Javascript
JavaScript通过prototype给对象定义属性用法实例
2015/03/23 Javascript
JavaScript编写连连看小游戏
2015/07/07 Javascript
AngularJS基础 ng-cut 指令介绍及简单示例
2016/08/01 Javascript
Javascript Function.prototype.bind详细分析
2016/12/29 Javascript
详解Vue.js分发之作用域槽
2017/06/13 Javascript
JS 中LocalStorage和SessionStorage的使用
2017/08/17 Javascript
JS异步函数队列功能实例分析
2017/11/28 Javascript
在vue项目中引入highcharts图表的方法(详解)
2018/03/05 Javascript
微信小程序仿RadioGroup改变样式的处理方案
2018/07/13 Javascript
微信小程序自定义组件之可清除的input组件
2018/07/17 Javascript
javascript异常处理实现原理详解
2020/02/17 Javascript
[02:01]2018完美盛典-开场舞《双子星》
2018/12/16 DOTA
windows下wxPython开发环境安装与配置方法
2014/06/28 Python
Python简单操作sqlite3的方法示例
2017/03/22 Python
python 内置函数filter
2017/06/01 Python
查看python安装路径及pip安装的包列表及路径
2019/04/03 Python
用python解压分析jar包实例
2020/01/16 Python
python定义类的简单用法
2020/07/24 Python
selenium携带cookies模拟登陆CSDN的实现
2021/01/19 Python
Molton Brown美国官网:奢华美容、香水、沐浴和身体护理
2020/09/02 全球购物
中学生自我鉴定
2014/02/04 职场文书
2014三八妇女节活动总结范文四篇
2014/03/09 职场文书
2014年保卫部工作总结
2014/11/21 职场文书
追悼会答谢词
2015/01/05 职场文书
技术员个人工作总结
2015/03/03 职场文书
工程项目合作意向书
2015/05/08 职场文书
MySQL创建管理HASH分区
2022/04/13 MySQL