迭代器模式可按顺序访问集合或聚合对象中的元素,同时又无须关注集合或聚合内部的实现细节。
此模式很有用,Python语言为其提供了内置支持。实现此模式有三种方法:
一、通过序列协议实现迭代器
实现名为__getitem__()的特殊方法,此方法可接受整数做下标参数,这个下标从0开始,如果
不能继续迭代了,那么应该抛出IndexError异常。
实现__getitem__()方法后,可以像列表一样用中括号加下标的形式访问元素,例如“list[0]”
这种方式只适合所求的值与下标有映射关系的场合,如果是“斐波那契”这种迭代关系就不适用了
class AtoZ: def __getitem__(self, index): if 0 <= index < 26: return chr(index + ord('A')) raise IndexError() # 每个字母都是根据下标直接计算 for letter in AtoZ(): print(letter, end="") ''' ABCDEFGHIJKLMNOPQRSTUVWXYZ '''
二、通过双参数iter()函数实现迭代器
还有个实现迭代器的办法是使用内置的iter()函数,但要传入两个参数,而不是像平常那样只传一个。
采用这种形式的时候,第一个参数必须是callable的(函数、绑定方法或其他callable对象),
第二个参数必须是个“标记值” ,没有"标记值"填None,Python才知道我们调用的是双参数iter()函数。
使用此方法制作的迭代器每次迭代都会调用callable对象(调用时不传参数),
只有当callable抛出StopIteration异常或返回标记值的时候,才停止迭代。
class Presidents: __names = ('George Washington[乔治·华盛顿]', 'John Adams[约翰·亚当斯]', 'Thomas Jefferson[托马斯·杰斐逊]', 'James Madison[詹姆斯·麦迪逊]', 'James Monroe[詹姆斯·门罗]', 'John Quincy Adams[约翰·昆西·亚当斯]', 'Andrew Jackson[安德鲁·杰克逊]', 'Martin van Buren[马丁·范布伦]', 'William Henry Harrison[威廉·亨利·哈里森]', 'John Tyler[约翰·泰勒]', 'James Knox Polk[詹姆斯·诺克斯·波尔克]', 'Zachary Taylor[扎卡里·泰勒]', 'Millard Fillmore[米勒德·菲尔莫尔]', 'Franklin Pierce[福兰克林·皮尔斯]', 'James Buchanan[詹姆斯·布坎南]', 'Abraham Lincoln[亚伯拉罕·林肯]', 'Andrew Johnson[安德鲁·约翰逊]', 'Ulysses Grant[尤里西斯·格兰特]', 'Rutherford B. Hayes[拉瑟福德·伯查德·海斯]', 'James A. Garfield[詹姆斯·加菲尔德]', 'Chester A Arthur[切斯特·艾伦·阿瑟]', 'Stephen Grover Cleveland[格罗佛·克利夫兰]', 'Benjamin Harrison[本杰明·哈里森]', 'Stephen Grover Cleveland[格罗佛·克利夫兰]', 'William McKinley[威廉·麦金利]', 'Theodore Roosevelt[西奥多·罗斯福]', 'William Howard Taft[威廉·霍华德·塔夫脱]', 'Thomas Woodrow Wilson[托马斯·伍德罗·威尔逊]', 'Warren Gamaliel Harding[沃伦·甘梅利尔·哈定]', 'John Calvin Coolidge[小约翰·卡尔文·柯立芝]', 'Herbert Clark Hoover[赫伯特·克拉克·胡佛]', 'Franklin Delano Roosevelt[富兰克林·德拉诺·罗斯福]', 'Harry S. Truman[哈里·S·杜鲁门]', 'Dwight David Eisenhower[德怀特·戴维·艾森豪威尔]', 'John Fitzgerald Kennedy[约翰·费茨杰拉德·肯尼迪]', 'Lyndon Baines Johnson[林登·贝恩斯·约翰逊]', 'Richard Milhous Nixon[理查德·米尔豪斯·尼克松]', 'Gerald Rudolph Ford [杰拉尔德·鲁道夫·福特]', 'James Earl Carter[吉米·卡特]', 'Ronald Wilson Reagan[罗纳德·威尔逊·里根]', 'George Herbert Walker Bush[乔治·赫伯特·沃克·布什]', 'Bill Clinton[比尔·克林顿]', 'George Walker Bush[乔治·沃克·布什]', 'Barack Hussein Obama[贝拉克·奥巴马]', 'Donald John Trump[唐纳德·特朗普]',) def __init__(self, first=None): self.index = (-1 if first is None else Presidents.__names.index(first) - 1) def __call__(self): self.index += 1 if self.index < len(Presidents.__names): return Presidents.__names[self.index] raise StopIteration() def main(): for president in iter(Presidents("Ronald Wilson Reagan[罗纳德·威尔逊·里根]"), None): print(president, end=" * ") print() for president in iter(Presidents("Ronald Wilson Reagan[罗纳德·威尔逊·里根]"), "Barack Hussein Obama[贝拉克·奥巴马]"): print(president, end=" * ") if __name__ == '__main__': main() ''' Ronald Wilson Reagan[罗纳德·威尔逊·里根] * George Herbert Walker Bush[乔治·赫伯特·沃克·布什] * Bill Clinton[比尔·克林顿] * George Walker Bush[乔治·沃克·布什] * Barack Hussein Obama[贝拉克·奥巴马] * Donald John Trump[唐纳德·特朗普] * Ronald Wilson Reagan[罗纳德·威尔逊·里根] * George Herbert Walker Bush[乔治·赫伯特·沃克·布什] * Bill Clinton[比尔·克林顿] * George Walker Bush[乔治·沃克·布什] * '''
三、通过迭代器协议实现迭代器
该协议要求类必须有名为__iter__()的特殊方法,而且该方法要返回迭代器对象。
迭代器对象的__iter__()方法必须返回迭代器自身,而__next__()方法则返回下一个元素,
如果没有元素可供迭代,则抛出StopIteration异常。
而要实现__iter__()方法,最容易地方式则是令其成为生成器,或令其返回生成器,因为生成器本身就符合迭代器协议。
创建一个“包(bag)”类,它与set类似,但其中可容纳重复的元素。为这个包增加迭代功能,现在我们用第三种方式来实现。
---包(bag)类:
class Bag: ''' Bag:与集合类似,只是元素允许重复 ''' def __init__(self, items=None): self.__bag = {} if items is not None: for item in items: self.add(item) def add(self, item): self.__bag[item] = self.__bag.get(item, 0) + 1 def __delitem__(self, item): # del bag['xx'] if self.__bag.get(item) is not None: self.__bag[item] -= 1 if self.__bag[item] <= 0: del self.__bag[item] else: raise KeyError(str(item)) def count(self, item): return self.__bag[item] def __len__(self): # len(bag) return sum(count for count in self.__bag.values()) def __contains__(self, item): # 'xx' in bag return item in self.__bag def __str__(self): return self.__bag.__str__()
--实现__iter__()方法,令其成为生成器:
class Bag: ''' Bag:与集合类似,只是元素允许重复 '''# 实现__iter__()方法,令其成为生成器 def __iter__(self): for item, count in self.__bag.items(): for _ in range(count): yield item if __name__ == '__main__': print({'a': 1, 'b': 1}.items()) # dict_items([('a', 1), ('b', 1)])
--实现__iter__()方法,令其返回生成器:
class Bag: ''' Bag:与集合类似,只是元素允许重复 ''' # 实现__iter__()方法,令其返回生成器 def __iter__(self): return (item for item, count in self.__bag.items() for _ in range(count)) if __name__ == '__main__': print({'a': 1, 'b': 1}.items()) # dict_items([('a', 1), ('b', 1)])
尽管这个Bag写的很好,但大家别忘了,标准库里还有个类已经实现了此功能,那就是collections.Counter