定义
- 提供一种方法顺序地访问一组聚合对象(一个容器)中的各个元素,而又不需要暴露该对象的内部细节。
- 迭代器一般至少有以下两种方法。
- 获取当前所指向的元素:
current()
- 将指针移至下一个元素:
next()
- 获取当前所指向的元素:
- 也可以增加一些方法,比如实现从后往前遍历。一些更为丰富的迭代器功能如下:
- 将指针移至起始的位置:
toBegin()
- 将指针移至结尾的位置:
toEnd()
- 获取当前所指向的元素:
current()
- 将指针移至下一个元素:
next()
- 将指针移至上一个元素:
previous()
- 将指针移至起始的位置:
迭代模式的抽象模型
代码框架
class BaseIterator:
"""迭代器"""
def __init__(self, data):
self.__data = data
self.toBegin()
def toBegin(self):
"""将指针移至起始位置"""
self.__curIdx = -1
def toEnd(self):
"""将指针移至结尾位置"""
self.__curIdx = len(self.__data)
def next(self):
"""移动至下一个元素"""
if (self.__curIdx < len(self.__data) - 1):
self.__curIdx += 1
return True
else:
return False
def previous(self):
"移动至上一个元素"
if (self.__curIdx > 0):
self.__curIdx -= 1
return True
else:
return False
def current(self):
"""获取当前的元素"""
return self.__data[self.__curIdx] if (self.__curIdx < len(self.__data) and self.__curIdx >= 0) else None
例子
class Customer:
"""客户"""
def __init__(self, name):
self.__name = name
self.__num = 0
self.__clinics = None
def getName(self):
return self.__name
def register(self, system):
system.pushCustomer(self)
def setNum(self, num):
self.__num = num
def getNum(self):
return self.__num
def setClinic(self, clinic):
self.__clinics = clinic
def getClinic(self):
return self.__clinics
class NumeralIterator:
"""迭代器"""
def __init__(self, data):
self.__data = data
self.__curIdx = -1
def next(self):
"""移动至下一个元素"""
if (self.__curIdx < len(self.__data) - 1):
self.__curIdx += 1
return True
else:
return False
def current(self):
"""获取当前的元素"""
return self.__data[self.__curIdx] if (self.__curIdx < len(self.__data) and self.__curIdx >= 0) else None
class NumeralSystem:
"""排号系统"""
__clinics = ("1号分诊室", "2号分诊室", "3号分诊室")
def __init__(self, name):
self.__customers = []
self.__curNum = 0
self.__name = name
def pushCustomer(self, customer):
customer.setNum(self.__curNum + 1)
click = NumeralSystem.__clinics[self.__curNum % len(NumeralSystem.__clinics)]
customer.setClinic(click)
self.__curNum += 1
self.__customers.append(customer)
print("%s 您好!您已在%s成功挂号,序号:%04d,请耐心等待!"
% (customer.getName(), self.__name, customer.getNum()))
def getIterator(self):
return NumeralIterator(self.__customers)
def testHospital():
numeralSystem = NumeralSystem("挂号台")
lily = Customer("Lily")
lily.register(numeralSystem)
pony = Customer("Pony")
pony.register(numeralSystem)
nick = Customer("Nick")
nick.register(numeralSystem)
tony = Customer("Tony")
tony.register(numeralSystem)
print()
iterator = numeralSystem.getIterator()
while iterator.next():
customer = iterator.current()
print("下一位病人 %04d(%s) 请到 %s 就诊。"
% (customer.getNum(), customer.getName(), customer.getClinic()))
testHospital()
python 中的迭代器
可以直接作用于 for 循环的对象统称为可迭代对象(Iterable) ,有以下两种
- 集合数据类型,如 list、tuple、dict、set、str 等。
- 生成器(Generator),包括
()
语法定义的生成器和带yield
的 generator 函数。
# 方法一:使用()定义生成器
gen = (x * x for x in range(10))
# 方法二:使用yield定义generator函数
def fibonacci(maxNum):
"""斐波那契数列的生成器"""
a = b = 1
for i in range(maxNum):
yield a # 相当于return,但是取走之后还向下运行
a, b = b, a + b
def testIterable():
print("方法一,0-9的平方数:")
for e in gen:
print(e, end="\t")
print()
print("方法二,斐波那契数列:")
fib = fibonacci(10)
for n in fib:
print(n, end="\t")
print()
print("内置容器的for循环:")
arr = [x * x for x in range(10)]
for e in arr:
print(e, end="\t")
print()
print()
print(type(gen))
print(type(fib))
print(type(arr))
生成器(Generator)不但可以作用于 for
循环,还可以被 next()
函数不断调用并返回下一个值,直到最后抛出 StopIteration
错误,表示无法继续返回下一个值。可以被 next()
函数调用并不断返回下一个值的对象称为迭代器(Iterator) 。
可以使用 isinstance()
来判断一个对象是否为 Iterable 对象或 Iterator 对象
from typing import Iterable, Iterator
# 方法一:使用()定义生成器
gen = (x * x for x in range(10))
# 方法二:使用yield定义generator函数
def fibonacci(maxNum):
"""斐波那契数列的生成器"""
a = b = 1
for i in range(maxNum):
yield a
a, b = b, a + b
def testIterable():
print("方法一,0-9的平方数:")
for e in gen:
print(e, end="\t")
print()
print("方法二,斐波那契数列:")
fib = fibonacci(10)
for n in fib:
print(n, end="\t")
print()
print("内置容器的for循环:")
arr = [x * x for x in range(10)]
for e in arr:
print(e, end="\t")
print()
print()
print(type(gen))
print(type(fib))
print(type(arr))
# 引入Iterable和Iterator
def testIsIterator():
print("是否为Iterable对象:")
print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance((1, 2, 3), Iterable))
print(isinstance(set([1, 2, 3]), Iterable))
print(isinstance("string", Iterable))
print(isinstance(gen, Iterable))
print(isinstance(fibonacci(10), Iterable))
print("是否为Iterator对象:")
print(isinstance([], Iterator))
print(isinstance({}, Iterator))
print(isinstance((1, 2, 3), Iterator))
print(isinstance(set([1, 2, 3]), Iterable))
print(isinstance("string", Iterator))
print(isinstance(gen, Iterator))
print(isinstance(fibonacci(10), Iterator))
testIsIterator()
运行结果:
是否为Iterable对象:
True
True
True
True
True
True
True
是否为Iterator对象:
False
False
False
True
False
True
True
- 生成器(Generator) 既是 Iterable 对象,也是 Iterator 对象。
- 列表(list)、字典(dict)、元组(tuple)、字符串是 Iterable 对象,却不是 Iterator 对象;集合(set)既是 Iterable 对象,也是 Iterator 对象。
- Iterator 对象可以被
next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误,表示无法继续返回下一个值。 - Iterable 对象不能被
next()
函数调用,可以用iter()
函数将 Iterable 对象转成 Iterator 对象,如iter([1,2,3])
。
def testNextItem():
print("将Iterable对象转成Iterator对象:")
l = [1, 2, 3]
itrL = iter(l)
print(next(itrL))
print(next(itrL))
print(next(itrL))
- 要使自定义的类具有 Iterable 属性,需要实现
__iter__
方法。 - 要使自定义的类具有 Iterator 属性,需要实现
__iter__
和__next__
方法。
from typing import Iterable, Iterator
class NumberSequence:
"""生成一个间隔为step的数字系列"""
def __init__(self, init, step, max=100):
self.__data = init
self.__step = step
self.__max = max
def __iter__(self):
return self
def __next__(self):
if (self.__data < self.__max):
tmp = self.__data
self.__data += self.__step
return tmp
else:
raise StopIteration
def testNumberSequence():
numSeq = NumberSequence(0, 5, 20)
print(isinstance(numSeq, Iterable))
print(isinstance(numSeq, Iterator))
# 先调 __iter__() 获取迭代器,再调用迭代器的__next__()。如果本身就是迭代器可以这么写。
for n in numSeq:
print(n, end="\t")
testNumberSequence()
模型说明
- 设计要点
- 了解容器的数据结构及可能的层次结构。
- 根据需要确定迭代器要实现的功能,如 next()、previous()、current()、toBegin()、toEnd() 中的一个或几个。
- 优缺点
- 优点:
- 迭代器模式将存储数据和遍历数据的职责分离。
- 简化了聚合数据的访问方式。
- 可支持多种不同的方式(如顺序和逆序)遍历一个聚合对象。
- 缺点:
- 需要额外增加迭代器的功能实现,增加新的聚合类时,可能需要增加新的迭代器。
- 优点:
应用场景
- 集合的内部结构复杂,不想暴露对象的内部细节,只提供精简的访问方式。
- 需要提供统一的访问接口,从而对不同的集合使用统一的算法。
- 需要为一系列聚合对象提供多种不同的访问方式。