1 模块简介

当你开始使用Python编程时,你或许已经使用了iterators(迭代器)和generators(生成器),你当时可能并没有意识到。在本篇博文中,我们将会学习迭代器和生成器是什么。当然,我们也会了解如何创建它们,在我们需要的时候,就可以创建属于我们自己的迭代器和生成器。

2 模块使用

2.1 迭代器

迭代器是一个允许你在一个容器上进行迭代的对象。Python的迭代器主要通过两个方法实现:__iter__和__next__。__iter__要求你的容器支持迭代。它会返回迭代器对象本身。如果你想创建一个迭代器对象,你还需要定义__next__方法,它将会返回容器的下一个元素。

注意: 在Python 2中,命名习惯有所不同,__iter__保持不变,__next__ 改为next。

为了对这些概念更加清晰,让我们回顾下面的两个定义:

  • 可迭代对象(iterable),只定义了__iter__方法;
  • 迭代器(iterator),定义了__iter__和__next__两个方法,__iter__返回迭代器本身,__next__方法返回下一个元素;

所有函数名中有双下划线的方法,都很神奇,你不需要直接调用__iter__或者__next__。你可以使用for循环或者转换为列表,Python就会自动替你调用这些方法。当然你或许还是想调用它们,那么你可以使用Python内置的函数iter和next方法。

Python 3 提供了一些序列类型,例如list、tuple和range。list是一个可迭代对象,但不是一个迭代器,因为它没有实现__next__方法。我们通过下面的例子,可以很容易发现这点。

>>> my_list = [1,2,3]
>>> next(my_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

当我们在这个例子中,尝试着调用列表的next方法,最终我们接收到一个TypeError异常信息,可以发现列表对象并不是一个迭代器。我们换一个思路,让我们看下面的代码:

>>> iter(my_list)
<list_iterator object at 0x7f6ec6b00f98>
>>> list_iterator = iter(my_list)
>>> next(list_iterator)
1
>>> next(list_iterator)
2
>>> next(list_iterator)
3
>>> next(list_iterator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

仅仅对列表调用Python内置的iter函数,就可以将列表转换为迭代器。当你对它调用next方法,直到它遍历完所有元素,就会收到StopIteration异常信息。让我们尝试将列表转换为迭代器,然后在循环中遍历它。

>>> for item in iter(my_list):
... print(item)
...
1
2
3

当你使用循环来遍历一个迭代器,你就不需要调用next方法,你也无需担心会收到StopIteration异常信息。

2.2 创建属于你的迭代器

有时候,你也想创建属于你的迭代器。Python很容易地就可以完成这个任务。正如上一章节提到,你需要做的就是在你的类里实现__iter__和__next__这两个方法。让我们创建一个迭代器,这个迭代器可以遍历字符串。

class MyIterator:
def __init__(self,letters):
self.letters = letters
self.position = 0
def __iter__(self):
return self
def __next__(self):
if self.position >= len(self.letters):
raise StopIteration
letter = self.letters[self.position]
self.position += 1
return letter if __name__ == "__main__":
i = MyIterator('abcd')
for item in i:
print (item)

对于这个例子,在我们的类中,我们仅仅需要实现三个方法。在初始化函数中,我们传入字符串,创建一个类变量用于引用这个字符串。我们也初始化了一个位置变量,以便于我们知道我们在字符串的位置。__iter__方法仅仅返回它本身,这就是是它所要做的。__next__是这个类中最重要的方法,我们首先检查位置是否超出字符串的长度,如果超出,就会抛出StopIteration异常信息。然后,我们提取出字符串上该位置的字符,将位置加1,最后返回该字符。

下面的例子中,我们创建一个无限的迭代器。一个无限的迭代器就是可以一直遍历。你要注意的是当调用这个迭代器时,如果你不加任何限制的话,它们将会导致死循环。

class Doubler():
def __init__(self):
self.number = 0
def __iter__(self):
return self
def __next__(self):
self.number += 1
return self.number * self.number if __name__ == "__main__":
doubler = Doubler()
count = 0
for number in doubler:
print (number)
if count > 5:
break
count += 1

在这段代码中,我们没有向迭代器中传入任何参数。我们仅仅以此为例进行说明。为了不陷入死循环,我们在开始遍历迭代器之前,加入一个计数器。最后,我们开始遍历,当计时器超过5,循环就会中断。

2.3 生成器

一个普通的Python函数经常返回一个值,无论它是列表、整数还是其他对象。但是如果你想调用一个函数,这个函数能否产生一系列值呢?这个就是生成器诞生的原因。生成器的工作机制是保存它所停止(或者说它已经产生)的位置,然后给主调函数返回一个值。不是返回一个调用函数的执行,生成器仅仅返回一个临时的控制返回。为了完成这个功能,生成器函数需要使用Python的 yield 语句。

注意: 在其它编程语言中,生成器可能叫做协同程序;

让我们先创建一个简单的生成器,

>>> def doubler_generator():
... number = 2
... while True:
... yield number
... number *= number
...
>>> doubler = doubler_generator()
>>> next(doubler)
2
>>> next(doubler)
4
>>> next(doubler)
16
>>> type(doubler)
<class 'generator'>

这个生成器仅仅是创建一个无限的序列。你可以一直对它调用next方法,它绝不会用完所有的值。因为你可以遍历生成器,因此,一个生成器也可以认为是一类迭代器,但是没有人会真正把它们联系在一起。其实,生成器也定义了__next__方法,我们观察上面的例子,这就是为什么next函数可以正常执行。

让我们来看另一个例子,这个例子中只产生3个元素,而不是无限的序列。

>>> def silly_generator():
... yield "Python"
... yield "Rocks"
... yield "So do you!"
...
>>> gen = silly_generator()
>>> next(gen)
'Python'
>>> next(gen)
'Rocks'
>>> next(gen)
'So do you!'
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

在这个例子中,我们创建了一个使用yield语句3次的生成器。在每个实例中,它产生不同的字符串。你可以认为yield是生成器的返回语句。无论何时你调用yield,函数都会中止,然后保存它的状态。然后它产生出值,这就是你在上面的例子里通过终端看到的输出。如果我们在函数中有一些变量,那么这些变量也会被保存。

当你看到StopIteration,你应该了解到你已经耗尽了这个迭代器。这就意味着这个迭代器已经用完了。你也可以在上面的迭代器部分看到这个特点。

无论何时我们调用next方法,生成器在它上次离开的地方开始,然后产生下一个值或者我们结束函数,生成器结束。如果你一直不调用next方法,这个状态就会永远保持下去。

让我们实例化这个生成器,然后使用循环来遍历它。

>>> gen = silly_generator()
>>> for item in gen:
... print(item)
...
Python
Rocks
So do you!

我们创建一个新的生成器实例,是因为当我们遍历完这个生成器的时候,我们不希望产生任何事情。这是因为我们已经遍历完这个生成器的实例的所有值。所以在这个例子中,我们创建一个实例,然后通过循环来遍历它,并打印产生出的值。当生成器遍历完,for循环会替我们处理StopIteration异常,然后中断循环。

生成器的一个优点就是它可以遍历很大的数据集,然后一次只返回一部分数据。下面的例子就是当我们打开一个文件,然后逐行返回数据,

with open("temp.txt") as fobj:
for line in fobj:
print line

当我们以这种方式遍历时,Python会将文件对象转换为一个生成器。这种方式允许我们处理空间太大以至于无法读入到内存的文件。对于任意的大数据集,对于这些大数据集,你需要块操作或者当你需要生成一个大数据集但是它将会填满你的内存,你就会发现生成器很有用处。

2.4 总结

到这里,你已经了解了什么是迭代器以及如何使用它。你也了解到可迭代对象和迭代器的区别。最后,我们学习了什么是生成器以及你如何使用它。例如,生成器很适合处理内存有效的数据。

3 Reference

Python 201

Python标准模块--Iterators和Generators的更多相关文章

  1. Python标准模块--threading

    1 模块简介 threading模块在Python1.5.2中首次引入,是低级thread模块的一个增强版.threading模块让线程使用起来更加容易,允许程序同一时间运行多个操作. 不过请注意,P ...

  2. Python标准模块--logging

    1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...

  3. Python标准模块--importlib

    作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 Python提供了importlib包作为标准库的一 ...

  4. Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures

    参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...

  5. python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...

  6. 【转】Python标准模块--importlib

    [转]Python标准模块--importlib 作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 P ...

  7. Python标准模块--logging(转载)

    转载地址:http://www.cnblogs.com/zhbzz2007/p/5943685.html#undefined Python标准模块--logging 1 logging模块简介 log ...

  8. python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程 什么是线程? 线程是cpu调度的最小单位 进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的 ...

  9. python标准模块(二)

    本文会涉及到的模块: json.pickle urllib.Requests xml.etree configparser shutil.zipfile.tarfile 1. json & p ...

随机推荐

  1. 使用ServiceStack构建Web服务

    提到构建WebService服务,大家肯定第一个想到的是使用WCF,因为简单快捷嘛.首先要说明的是,本人对WCF不太了解,但是想快速建立一个WebService,于是看到了MSDN上的这一篇文章 Bu ...

  2. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  3. python 数据类型 ----字典

    字典由一对key:value 组成的 python中常用且重量级的数据类型 1. key , keys, values 字典由一对key:value 组成的 python中常用且重量级的数据类型 1. ...

  4. 【SAP业务模式】之ICS(七):IDOC配置

    这是ICS业务模式系列的最后一篇了,主要讲解IDOC的配置. 一.指定EDI传输的供应商逻辑地址 事务代码:WEL1 注意:上面逻辑地址是生产公司+内部客户.有以下两种情形: 1.如果内部客户都是纯数 ...

  5. H3 BPM让天下没有难用的流程之产品概述

    一.产品简介 BPM(Business Process Management),是指根据业务环境的变化,推进人与人之间.人与系统之间以及系统与系统之间的整合及调整的经营方法与解决方案的IT工具. H3 ...

  6. 不要着急改代码,先想想--centos 6.8下编译安装tmux

    诸位读者新年好,2017开年第一篇博客,请允许我先问候一下看到这篇博客的诸位.写博客是我2017年定下的目标之一,希望我会坚持下去. 最近打算尝试一下tmux这个神器,于是有了这一篇关于思维方式的Bl ...

  7. mono for android 获取手机照片或拍照并裁剪保存

    axml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

  8. JAVA通信系列三:Netty入门总结

    一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...

  9. ASP.NET MVC Model绑定(五)

    ASP.NET MVC Model绑定(五) 前言 前面的篇幅对于IValueProvider的获取位置和所处的生成过程做了讲解,本篇将会对IValueProvider的使用做个基础的示例讲解,读完本 ...

  10. 高薪诚聘熟悉ABP框架的.NET高级开发工程师(2016年7月28日重发)

    招聘单位是ABP架构设计交流群(134710707)群主阳铭所在的公司-上海运图贸易有限公司 招聘岗位:.NET高级开发工程师工作地点:上海-普陀区 [公司情况]上海运图贸易有限公司,是由易迅网的创始 ...