初学时,被可迭代对象,迭代器和生成器绕的云里雾里。
首先来区分一下这几个概念。
迭代器(iterator)
迭代器是指内部包含了 iter 和next 方法的类。
iter 函数返回对象本身,而 next 方法则是返回下一个元素。
#依次输出迭代器中的元素
class An_Iterator(object):
def __init__(self,data):
self.data=data
self.index=0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.data):
raise StopIteration
value=self.data[self.index]
self.index += 1
return value
string=An_Iterator('abc')
next(string) #也可以写作 string.__next__() 得到元素 'a'
next(string) # 'b'
next(string) # 'c'
next(string) # 抛出异常 StopIteration
在上面的代码块中,先创建了 An_Iterator 的类,内部有个iter的方法,返回对象本身。当访问内部的next方法时,返回下一个元素。当超出了范围后,返回异常。
实际上满足下面两个条件的都可以称为迭代器
(1)iter() 方法返回迭代器对象本身。
(2)next() 方法会返回下一个迭代器对象。如果数据没有了,抛出 StopIteration 异常。
for i in string:
print(i)
一般不用next方法来返回,可以用 for 循环来遍历循环。在 for 语句的幕后,其实 python 先对对象调用了 iter 函数。这个函数返回了迭代器对象,这边也就是返回了本身。返回的对象能够使用 next 方法,调用下一个元素。
生成器(generator)
生成器是一种特殊的迭代器,也含有iter和next方法。
生成器的构造
生成器可以通过2种方法进行构造。
(1) 将函数定义中的 return 变成 yield,即可定义一个生成器
每次语句执行到 yield 时,就跳出了定义的函数。下一次再执行,则从上次中断的地方继续执行。所以在迭代生成器时,每一次执行都可以保留上一次的状态,而不是像普通方法那样,遇到 return 就返回结果,下一次执行只能再次重复上一次的流程。
def squares(n=10):
'''返回1:n^2'''
for i in range(1,n+1):
yield i**2
a=squares(3) #此处并未执行squares中的代码,只是创建了一个生成器对象
next(a) #1
next(a) #4
next(a) #9
for element in a:
print(element,end=' ')
#1 4 9
再看下一个例子,更深入理解 yield 的用法。
def counter_generator(low, high):
while low <= high:
yield low
low += 1
for i in counter_generator(5,10):
print(i, end=' ')
# 5 6 7 8 9 10
在 while 循环中,当它到达 yield 语句时,将返回 low 值,并退出生成器。在第二次下一次调用期间,发生器在之前中断的地方恢复,然后low+1。再继续使用 while 循环,并再次进入 yield 语句。
之前提到生成器和迭代器都有iter和next方法,可以通过dir() 函数查看。
dir(string)
dir(a)
#能在输出的列表中找到__iter__和__next__方法
生成器最大的好处就是能够处理很大的数据而不占用很大内存, 因为它是一边计算一边生成元素的,而不是一下子把所有的元素都导入到内存中,所以大大节省了内存,理论上可以产生包含无限元素的生成器。
(2)生成器表达式
在列表表达式中,以中括号的形式来创建,生成器表达式只要把中括号替换成小括号即可。
gen=(x**2 for x in range(10))
for i in gen:
print(i)
生成器有一个问题就是不能重复使用,当执行完上面的代码后,再执行依次,不会有内容输出。
可迭代对象(Iterable)
可迭代对象有iter方法,能够返回一个迭代器。
#还是上面这样一个迭代器
class An_Iterator(object):
def __init__(self,data):
self.data=data
self.index=0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.data):
raise StopIteration
value=self.data[self.index]
self.index += 1
return value
class It(object):
def __init__(self,data):
self.data=data
def __iter__(self):
return An_Iterator(self.data)
b=It('abcd') #b是一个可迭代对象
dir(b) #输出中,b包含了__iter__,而没有__next__方法
上述的代码中,定义了一个 An_Iterator 的迭代器,定义了 It 类,这个类包含了iter方法,并且返回了迭代器An__Iterator,符合可迭代的对应,b是 It 的一个实例化, 所以b是可迭代对象。
在 python 的数据结构中,字符串,tuple,list,dict,set均为可迭代对象,而文件对象是迭代器对象。
对可迭代对象,也可以通过for循环遍历,但是不能使用next方法
因为 for 语句执行过程中,先将对象使用iter方法,这时候就返回一个迭代器,而迭代器自然可以用next方法。
判断迭代器和可迭代对象
from collections.abc import Iterable,Iterator
list_1=[1,2,3]
isinstance(list_1,Iterable) #True
isinstance(list_1,Iteration) #False
从上面的结果也可以看到,列表是可迭代对象,而不是迭代器。