天天看点

Python中可迭代对象和迭代器

什么叫可迭代对象?

  • 不专业:可以直接作用于

    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)
           

# 测试结果如下:

张三
李四
王五