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 getopt 参数处理小示例
Jun 09 Python
python中的字典使用分享
Jul 31 Python
Python数据结构与算法之列表(链表,linked list)简单实现
Oct 30 Python
快速查询Python文档方法分享
Dec 27 Python
python如何为被装饰的函数保留元数据
Mar 21 Python
对Python获取屏幕截图的4种方法详解
Aug 27 Python
Python Opencv轮廓常用操作代码实例解析
Sep 01 Python
使用AJAX和Django获取数据的方法实例
Oct 25 Python
pycharm 快速解决python代码冲突的问题
Jan 15 Python
python多线程爬取西刺代理的示例代码
Jan 30 Python
python爬取youtube视频的示例代码
Mar 03 Python
Matplotlib绘制条形图的方法你知道吗
Mar 21 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 文件缓存函数
2011/10/08 PHP
PHP获取文件后缀名的三个函数
2012/10/15 PHP
深入for,while,foreach遍历时间比较的详解
2013/06/08 PHP
PHP+Memcache实现wordpress访问总数统计(非插件)
2014/07/04 PHP
PHP类与对象后期静态绑定操作实例详解
2018/12/20 PHP
js中cookie的添加、取值、删除示例代码
2013/10/21 Javascript
JavaScript中使用Math.PI圆周率属性的方法
2015/06/14 Javascript
分享经典的JavaScript开发技巧
2015/11/21 Javascript
HTML5 js实现拖拉上传文件功能
2020/11/20 Javascript
Vue.js学习教程之列表渲染详解
2017/05/17 Javascript
js blob类型url的视频下载问题的解决
2019/11/29 Javascript
JS实现多选框的操作
2020/06/24 Javascript
WebStorm中如何将自己的代码上传到github示例详解
2020/10/28 Javascript
[03:02]辉夜杯主赛事第二日 每日之星
2015/12/27 DOTA
[00:59]DOTA2英雄背景故事——上古巨神
2020/06/28 DOTA
Python3 伪装浏览器的方法示例
2017/11/23 Python
Python数据结构与算法之常见的分配排序法示例【桶排序与基数排序】
2017/12/15 Python
wtfPython—Python中一组有趣微妙的代码【收藏】
2018/08/31 Python
Python基于mysql实现学生管理系统
2019/02/21 Python
python暴力解压rar加密文件过程详解
2019/07/05 Python
Pytorch中膨胀卷积的用法详解
2020/01/07 Python
python实现实时视频流播放代码实例
2020/01/11 Python
Tensorflow 使用pb文件保存(恢复)模型计算图和参数实例详解
2020/02/11 Python
python中编写函数并调用的知识点总结
2021/01/13 Python
Django url 路由匹配过程详解
2021/01/22 Python
英国著名的茶叶品牌:Whittard of Chelsea
2016/09/22 全球购物
俄罗斯韩国化妆品网上商店:Cosmasi.ru
2019/10/31 全球购物
意大利在线高尔夫商店:Online Golf
2021/03/09 全球购物
linux系统都有哪些运行级别
2012/04/15 面试题
介绍一下Java中标识符的命名规则
2014/02/03 面试题
大学生学习生活的自我评价
2013/11/01 职场文书
银行开业庆典方案
2014/02/06 职场文书
岗位职责说明书
2014/05/07 职场文书
教师节晚会主持词
2015/06/30 职场文书
聘任书范文大全
2015/09/21 职场文书
职场中的你,辞职信写对了吗?
2019/06/26 职场文书