一、迭代器
迭代器的特性:
迭代是Python中最强有力的特性之一,可以把迭代看成是一种处理序列中元素的方式。
可以直接作用于for循环的对象统称为可迭代对象(Iterable)。
可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
所有的Iterable均可以通过内置函数iter()来转变为Iterator。
出可迭代对象的个数会报错StopIteration,是一个结束信号
迭代器的优点:
1、迭代器提供一种不依赖于索引的取值方式,这样就可以遍历那些没有索引的对象了(字典、集合、文件)
2、迭代器和列表比较,迭代器是惰性计算,更节约内存
迭代器的缺点:
1、永远不能获取迭代器的长度,使用不如列表索引取值灵活
2、一次性的,只能往后取值,不能往前,不能像索引一样取得指定位置的值
二、查看可迭代和迭代器的方法
from collections import Iterable,Iterator
s='hello'
l=[1,2,3]
t=(1,2,3)
d={'a':1}
set1={1,2,3,4}
f=open('a.txt')
#都是可迭代,只要对象有__iter__()方法,都是可迭代的
s.__iter__()
l.__iter__()
t.__iter__()
d.__iter__()
set1.__iter__()
f.__iter__()
#查看是否可以迭代的
print(isinstance(s,Iterable))
#查看是否是迭代器,只有文件是迭代器
f.__next__()
print(isinstance(f,Iterator))
三、手动访问迭代器中的元素
1、问题
我们要处理某个可迭代对象中的元素,但是基于某种原因不能也不想使用for循环。
2、解决方案
要手动访问可迭代对象中的元素,可以使用next()函数,然后自己编写代码来捕获stopiteration异常。例如:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
with open('passwd','r',encoding='utf8') as f:
while True:
try:
line=next(f)
print(line,end='')
except StopIteration:
break
#一般来说,stopiteration异常是用来通知我们迭代结束的,但是,如果是手动执行next(),也可以命令他返回一个值,比如None,示例如下:
with open('passwd','r',encoding='utf8') as f:
while True:
line=next(f,None)
if line is None:
break
print(line,end='')
大多数情况下,我们会用for循环来访问可迭代对象中的元素,但是,偶尔也会碰到需要对底层迭代机制做更精细情况的控制。因此,了解迭代实际发生了些什么是很有必要的。for 循环也是先使用iter方法把要循环的对象生成一个迭代器,然后去遍历这个迭代器,遍历迭代器里的每个元素
>>> items=[1,2,3]
>>> i=iter(items) #===> i.__iter__()
>>> next(i) #===> i.__next__()
1
>>> next(i)
2
>>> next(i)
3
>>> next(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
四、生成器
生成器是一个包含yield关键字的函数,当它被调用时,在函数中的代码不会执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,直到遇到一个yield或者return语句,yield语句一位着会生成一个值,return语句一位着生成器要停止执行(不在生成任何东西,return语句只有在一个生成器中使用时才能进行无参数调用)换句话说,生成器是由两部分组成:生成器的函数和生成器的迭代器,生成器的函数使用def定义,包括yield部分,生成器的迭代器是这个函数返回的部分。
def countdown(n):
print('starting to count from',n)
while n>0:
yield n
n-=1
print('Done!')
g=countdown(5)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
#执行结果如下
starting to count from 5
5
4
3
2
1
#函数中出现了yield语句,就会把这个函数转换成生成器。与普通的函数不同,生成器只会在响应迭代操作是才会运行。调用时需要使用next方法才能执行,没执行一次,当遇到下一个yield时就会停止,下次执行next时,将从当前yield往下执行,知道遇到一个yield
生成器的本质就是一个迭代器,同时也是一个函数,调用的方式和函数类似,yield也相当于函数中的return
yield和return的区别:return只能返回一次函数就会彻底结束,而yield能返回多次值
yield到底干了什么事情:
1、yield把函数变成生成器(迭代器),把iter和next方法封装在函数内部
2、函数在暂停一及继续下一次运行时的状态是yield保存
五、协程
from urllib.request import urlopen
def init(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
next(res)
return res
return wrapper
@init
def get():
while True:
url=yield
res=urlopen(url).read()
print(res)
g=get() #g是一个迭代器
# next(g) #这里使用一个init的装饰器,自动做了next的工作
g.send("http://www.baidu.com") #使用send把括号内的URL传递给yield
g.send('http://www.python.org')
g.send('http://www.163.com')
协程函数,是把yield改为表达式的方式,改过send代替next给yield传值
e.send于next(e)的区别
1、如果函数内yield是表达式形式,那么必须先next(e),让生成器在第一个yield位置处等着
2、二者的共同之处都可以让函数在上次暂停的位置继续运行,不一样的地方在于,send在触发一下次代码的执行时,给yield传值