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 ...
随机推荐
- EasyMesh - A Two-Dimensional Quality Mesh Generator
EasyMesh - A Two-Dimensional Quality Mesh Generator eryar@163.com Abstract. EasyMesh is developed by ...
- svn 常用命令总结
svn 命令篇 svn pget svn:ignore // 查看忽略项 svn commit -m "提交说明" // 提交修改 svn up(update) // 获取最新版本 ...
- mybatis_常用标签
1.<where></where>标签的作用 可以动态的添加where关键字 可以自动去掉第一个拼接条件的and关键字 <where> <if test=&q ...
- 前端性能优化的另一种方式——HTTP2.0
最近在读一本书叫<web性能权威指南>谷歌公司高性能团队核心成员的权威之作. 一直听说HTTP2.0,对此也仅仅是耳闻,没有具体研读过,这次正好有两个篇章,分别讲HTTP1.1和HTTP2 ...
- 基于Composer Player 模型加载和相关属性设置
主要是基于达索软件Composer Player.的基础上做些二次开发. public class ComposerToolBarSetting { public bool AntiAliasingO ...
- python 数据类型 ---文件一
1.文件的操作流程: 打开(open), 操作(read,write), 关闭(close) 下面分别用三种方式打开文件,r,w,a 模式 . "a"模式将不会覆盖原来的文件内容, ...
- 多线程同步工具——Lock
本文原创,转载请注明出处. 参考文章: <"JUC锁"03之 公平锁(一)> <"JUC锁"03之 公平锁(二)> 锁分独占锁与共享锁, ...
- Java—恶心的java.lang.NumberFormatException解决
项目中要把十六进制字符串转化为十进制, 用到了到了Integer.parseInt(str1.trim(), 16):这个是不是后抛出java.lang.NumberFormatException异常 ...
- github免输用户名/密码SSH登录的配置
从github上获取的,自己整理了下,以备后用. Generating an SSH key mac windows SSH keys are a way to identify trusted co ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...