前言:
关于可迭代对象和迭代器的关系和构建一直理解的都不太深入,今天就自己来深刻扒一扒他们之间的关系!
可迭代对象(iterable):
python中的可迭代对象包括:
- list, dict, tuple, str, 用dir()函数查看他们的对象方法都会发现有__iter__()在里面.
- 迭代器和生成器也属于可迭代对象
只要有__iter__()方法的对象都是可迭代对象(注意和普通的实例对象区分开).用下面的例子来说明:
先介绍如何来判断是否为可迭代对象:从collections中引入Iterable类
form collections import Iterable
class A:
def __init__(self):
pass
class B(A):
def __iter__(self):
pass
a = A()
b = B()
isinstance(a, Iterable) #返回False
isinstance(b, Iterable) #返回True
可以看出: A类的实例对象a不是可迭代对象, B类继承了A类, 并且又构建了自己的__iter__方法, 所以B的实例对象b是可迭代对象
那么可迭代对象和迭代器又有什神马关系了???
迭代器(iterator):
如果一个可迭代对象有__next__()方法, 那其就是一个迭代器,我们继续用例子来说明:
先介绍如何来判断是否为迭代器:从collections中引入Iterator类
from collections import iterator
class C(B):
def __next__(self):
pass
c = C()
isinsatnce(a, Iterable) #返回False
isinstance(b, Iterator) #返回False
isinstance(c, Iterator) #返回True
以上例子可以看出: B类的实例对象b是一个可迭代对象但不是一个迭代器. C类继承了B类, 且C类构建了自己的__next__()方法, 所以C的实例对象c是一个迭代器, 但是A类的实例对象a既不是可迭代对象, 也不是迭代器.
下面介绍__iter__()和__next__()两种魔法方法.
__iter __() 需要返回的是当前对象的 迭代器类的实例, 可以存在两种用法
**(1)**用在可迭代对象内: 看如下示例
class D:
def __init__(self):
self.a = [1, 2, 3]
def __iter__(self):
return iter(self.a) #slef.a是一个列表(可迭代对象), iter(self.a)调用了列表的__iter__()方法, 返回迭代器类的实例
class E:
def __init__(self):
self.a = [1, 2, 3]
def __iter(self):
return self #self就是该实例对象本身, 只是一个可迭代对象实例, 不是一个迭代器实例
d = D()
e = E()
dd = iter(d) #iter()函数会调用d示例对象的__iter__()方法
ee = iter(e) # 会报错,TypeError: iter() returned non-iterator of type 'E'
isinstance(d, iterator) #返回False, d只是一个可迭代对象, 不是迭代器实例
isinstance(dd, iterator) #返回True
**(2)**还有一个例子:
class F:
def __init__(self):
pass
def __iter__(self):
return self #返回self自己, sel本身就是一个迭代器类实例
def __next__(self):
pass
f = F()
#f是一个迭代器类实例
ff = iter(f)
isinstance(f, iterator) #返回True
isinstance(ff, iterator) #返回True
__next __():返回迭代器的每一次迭代
例如下定义一个斐波拉切类
class Fib:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + sefl.b
return self.a
fib = Fib()
fib_iter = iter(fib) #调用__iter__()方法, 返回一个迭代器类实例
#next函数调用迭代器的__next__()方法,每次返回一个迭代值
next(fib_iter) # 1
next(fib_iter) # 1
next(fib_iter) # 2
next(fib_iter) # 3
next(fib_iter) # 5
在python中, 序列(list, str,tuple)以及dict都是属于可迭代对象。 且调用iter()函数之后都会返回他们的迭代器实例
在python中, 使用for函数可以遍历可迭代对象,步骤如下:
1): 首先传入一个可迭代对象
2): python会使用iter()函数调用可迭代对象的__iter__()方法
3): 如果iter()函数返回了一个迭代器实例,若iter()返回的不是一个迭代器实例,则会返回一个类型错误TypeError: iter() returned non-iterator of type ‘A’
4): 逐次地调用Next()方法, 每次返回一个迭代,直到结束为止!
另外: 当然也可以直接给for传入一个可迭代对象
具体实例如下:
class A:
def __init__(self, max):
self.max = max
self.a, self.b = 0, 1
def __iter__(self):
return self
class Fib(A):
def __init__(self, max):
super().__init__(max)
def __next__(self):
if self.b < self.max:
self.a = self.b
self.b = self.a + self.b
return self.a
raise StopIteration()
a = A(10) #a是一个可迭代对象, 但iter(a)不是一个迭代器
for i in a:
print(i) #会返回错误:TypeError: iter() returned non-iterator of type 'A'
#下面两个都会成功打印出来
fib = Fib(10)
for i in fib:
print(i)
_fib = iter(fib)
for i in _fib: #此时的_fib就是一个迭代器实例,而不是可迭代对象
print(i)
最后来说一下生成器yield
yield
由于迭代器必须要在类中定义实现,为了能够在函数中也可以实现迭代器, 就有了yield
yield 的作用就是把一个函数变成一个generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.
函数每次运行到yiled之后就会挂起, 然后下一次再被调用,再继续挂起,直到最后结束,自动抛出 StopIteration 异常。具体代码如下:
def fib(max):
a, b = 0, 1
while(max > b):
a = b,
b = a+b
yield a
_fib = fib(10)
isinstance(_fib, Iterator) #返回True,表示返回的生成器是一个迭代器(iterator)
for i in _fib :
print(i)
#输出1, 1, 2, 3, 5, 8
#也可以调用该迭代器的__Next__()方法
Next(_fib) #1
Next(_fib) #1
Next(_fib) #2
Next(_fib) #3
Next(_fib) #5
Next(_fib) #8
Next(_fib) # 返回StopIteration