Python标准模块--Iterators和Generators
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标准模块--Iterators和Generators的更多相关文章
- Python标准模块--threading
1 模块简介 threading模块在Python1.5.2中首次引入,是低级thread模块的一个增强版.threading模块让线程使用起来更加容易,允许程序同一时间运行多个操作. 不过请注意,P ...
- Python标准模块--logging
1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...
- Python标准模块--importlib
作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 Python提供了importlib包作为标准库的一 ...
- Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...
- python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...
- 【转】Python标准模块--importlib
[转]Python标准模块--importlib 作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 P ...
- Python标准模块--logging(转载)
转载地址:http://www.cnblogs.com/zhbzz2007/p/5943685.html#undefined Python标准模块--logging 1 logging模块简介 log ...
- python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程 什么是线程? 线程是cpu调度的最小单位 进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的 ...
- python标准模块(二)
本文会涉及到的模块: json.pickle urllib.Requests xml.etree configparser shutil.zipfile.tarfile 1. json & p ...
随机推荐
- .net erp(办公oa)开发平台架构概要说明之表单设计器
背景:搭建一个适合公司erp业务的开发平台. 架构概要图: 表单设计开发部署示例图 表单设计开发部署示例说明1)每个开发人员可以自己部署表单设计至本地一份(当然也可以共用一套开发环境,但是如 ...
- python与c互相调用
虽然python开发效率很高,但作为脚本语言,其性能不高,所以为了兼顾开发效率和性能,通常把性能要求高的模块用c或c++来实现或者在c或c++中运行python脚本来处理逻辑,前者通常是python中 ...
- spring注解源码分析--how does autowired works?
1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...
- bash字符串操作
参考 http://www.cnblogs.com/chengmo/archive/2010/10/02/1841355.html 问题:bash怎么提取字符串的最后一位?例如python中strin ...
- BPM与 SAP & Oracle EBS集成解决方案分享
一.需求分析 SAP和Oracle EBS都是作为全球顶级的的ERP产 品,得到了众多客户的青睐.然而由于系统庞大.价格昂贵以及定位不同,客户在实施过程中经常会面临以下困惑: 1.SAP如何实现&qu ...
- BPM SharePoint解决方案分享
一.需求分析 SharePoint作为微软推出的协同类平台产品,为客户提供了门户.内容.文档.流程.社区.搜索.BI等一系列的解决方案,然而其流程功能由于设计理念差异,不能完全满足客户的需求,主要原因 ...
- 信息安全-1:python之playfair密码算法详解[原创]
转发注明出处: http://www.cnblogs.com/0zcl/p/6105825.html 一.基本概念 古典密码是基于字符替换的密码.加密技术有:Caesar(恺撒)密码.Vigenere ...
- windows10安装mysql5.7.17是这样安装的吗?
操作 全允许
- 基于Ubuntu Hadoop的群集搭建Hive
Hive是Hadoop生态中的一个重要组成部分,主要用于数据仓库.前面的文章中我们已经搭建好了Hadoop的群集,下面我们在这个群集上再搭建Hive的群集. 1.安装MySQL 1.1安装MySQL ...
- centos 7 安装mono 和 monodevelop
本次所有操作在root模式下 1.执行 rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3 ...