设计模式---行为型设计模式【迭代器模式】

迭代器模式可按顺序访问集合或聚合对象中的元素,同时又无须关注集合或聚合内部的实现细节。

此模式很有用,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

原文地址:https://www.cnblogs.com/staff/p/11653569.html