什么叫可迭代对象?
- 不专业:可以直接作用于
for
循环的对象,统称为可迭代对象:Iterable
- 稍微专业:通过 Python 内置方法
isinstance(obj, Iterable)
进行判断, 若返回为True
则是,反之则不是; - 专业解释:该对象含有
__iter__
方法则称为可迭代对象;
from collections import Iterable, Iterator
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
print("c 是否是可迭代对象:", isinstance(c, Iterable))
# 测试结果如下:
# 实例 c 中不含有 __iter__ 方法时,判断显示为 False,即不可迭代对象
c 是否是可迭代对象: False
# 当给类 Classmate 加上 __iter__
方法时
from collections import Iterable, Iterator
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
def __iter__(self):
pass
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
print("c 是否是可迭代对象:", isinstance(c, Iterable)) # 当注释掉类中的 __iter__ ,这里判断会变为 False
# 测试结果如下:
# 实例 c 中不含有 __iter__ 方法时,判断显示为不可迭代对象
c 是否是可迭代对象: True
什么叫迭代器?
- 不专业1:可以被
next()
函数调用并不断返回下一个值的对象称为迭代器; - 不专业2:通过
iter()
函数作用于可迭代对象之后生成的对象称为迭代器; - 稍微专业:通过 Python 内置方法
isinstance(obj, Iterator)
进行判断, 若返回为True
则是,反之则不是; - 专业解释:通过
iter()
函数作用于可迭代对象之后生成的对象,并且该对象含有__iter__
和__next__
方法;
from collections import Iterable, Iterator
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
def __iter__(self):
# pass
return ClassIterator()
class ClassIterator(object):
def __iter__(self):
pass
def __next__(self):
pass
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
print("c 是否是可迭代对象:", isinstance(c, Iterable)) # 当注释掉类中的 __iter__ ,这里判断会变为 False
print("c 是否是迭代器:", isinstance(c, Iterator))
d = iter(c)
print("d 是否是迭代器:", isinstance(d, Iterator))
# 测试结果如下:
c 是否是可迭代对象: True
c 是否是迭代器: False
d 是否是迭代器: True # 当注释掉 类 ClassIterator 中的任意一个方法,该返回值为 False 或报错
那么问题来了,什么时候可以用 for 循环去从可迭代对象或者迭代器中进行取值呢?
先弄清楚,不论是 for 循环是从可迭代对象还是迭代器中取值,都是调用的该对象的 __next__
方法返回的值,那问题来了,可迭代对象中比如上述代码中的类 Classmate
没有 __next__
方法就不能取值了?这里答案是否定的,因为类 Classmate
中还有 __iter__
方法, 调用该方法若返回的对象中有 __next__
方法,仍旧可以取该方法的返回值;
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
def __iter__(self):
# pass
return ClassIterator()
class ClassIterator(object):
def __iter__(self):
pass
def __next__(self):
return 111
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
for i in c:
print(i)
# 测试结果如下:
控制台会不断打印类 ClassIterator
中的 __next__
方法的输出 111,这个结果肯定不是我们希望的,我们希望打印我们添加的名称;
略
于是我们继续改造代码如下:
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
def __iter__(self):
# pass
return ClassIterator(self)
class ClassIterator(object):
def __init__(self, obj):
self.obj = obj
self.num = 0
def __iter__(self):
pass
def __next__(self):
res = self.obj.name[self.num]
self.num += 1
return res
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
for i in c:
print(i)
# 测试结果如下:
Traceback (most recent call last):
张三
File "/Users/wawa/Desktop/code/03协程/迭代器.py", line 46, in <module>
李四
for i in c:
王五
File "/Users/wawa/Desktop/code/03协程/迭代器.py", line 32, in __next__
res = self.obj.name[self.num]
IndexError: list index out of range
继续优化:
class Classmate(object):
def __init__(self):
self.name = list()
def add(self, name):
self.name.append(name)
def __iter__(self):
# pass
return ClassIterator(self)
class ClassIterator(object):
def __init__(self, obj):
self.obj = obj
self.num = 0
def __iter__(self):
pass
def __next__(self):
if self.num < len(self.obj.name):
res = self.obj.name[self.num]
self.num += 1
return res
else:
raise StopIteration
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
for i in c:
print(i)
# 测试结果如下:
张三
李四
王五
到这里了好像都实现了,那么问题又来了,可以把上面的两个类用一个类实现吗?
真的是一个好问题~其实这里也是手写迭代器实现 for 循环的逻辑,继续优化代码如下:
class Classmate(object):
def __init__(self):
self.name = list()
self.num = 0
def add(self, name):
self.name.append(name)
def __iter__(self):
# pass
# return ClassIterator(self)
return self
def __next__(self):
if self.num < len(self.name):
res = self.name[self.num]
self.num += 1
return res
else:
raise StopIteration
if __name__ == '__main__':
c = Classmate()
c.add("张三")
c.add("李四")
c.add("王五")
for i in c:
print(i)
# 测试结果如下:
张三
李四
王五