python 设计模式之迭代器模式

1. 迭代器模式

迭代器模式让你在不暴露集合底层实现形式的情况下遍历集合中的所有元素。你或许并不了解python中字典和集合的实现形式,但是你能用for循环对他们进行遍历,对列表也是如此,尽管他们的底层实现是完全不一样的,但对于使用者,遍历的方法确实相同的。

下面的程序向你演示如何用相同的方法遍历列表和集合

lst = [1, 2, 3, 4]
data_set = set([4, 5, 6, 7])

lst_iter = iter(lst)            # 获取迭代器
set_iter = iter(data_set)

def iter_it(iter_obj):
    while True:
        try:
            data = next(iter_obj)       # 遍历迭代器
            print(data)
        except:
            break

iter_it(lst_iter)
print('*'*20)
iter_it(set_iter)

我没有使用for循环,而是通过iter函数获取列表和集合的迭代器,通过next函数获取迭代器里的下一个元素,for循环内部所做的事情与上面的代码所表达的逻辑几乎是相同的。

列表和集合,底层的实现是不相同的,但我们可以用相同的方法对他们进行遍历,这中间就是迭代器模式在起作用,设想,如果因为底层实现不同,遍历的方式就不同,那对于我们写代码来说将是一场灾难。

如果集合底层实现用的是树,就需要考虑用深度优先遍历或是广度优先遍历,如果底层实现的方法是hashtable,那么就需要考虑像遍历列表一样进行遍历,底层实现的差异所造成的遍历方式的不同都被迭代器模式所屏蔽掉了,你只需要获取到迭代器就能够遍历集合,无需关心底层的具体实现, 这就是迭代器模式的精髓。

掌握了迭代器模式的精髓,那么只要能够在代码里体现这种原则,目的,就可以认为是实现了迭代器模式,模式不是标准,不是非得怎样才行,你看到的文章里设定了各种名词以及代码里的所展示出的固定格式,都只是为了更容易去用代码阐述某种模式,但不要被限定在代码的格式中,去努力理解一种模式的设计初衷和目的。

2. 编写遍历对象属性的迭代器

迭代器模式的关键是提供一个迭代器去遍历集合,不管集合底层如何实现,只要遍历方式一样就达到目的了。

接下来的阅读,你需要掌握一些有关python迭代器的知识,否则下面的代码将很难理解,你看先阅读我的另一篇教程一文看懂python的迭代器和可迭代对象

我先定义一个类

class Test():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'{self.name} 今年 {self.age}'

    def print(self):
        pass

现在,要求你提供一种能够遍历对象所有属性的迭代器,咋弄呢,很简单

class ObjAttrIterator():
    def __init__(self, obj):
        self.obj = obj
        self.attrs = list(obj.__dict__.keys())      # 所有属性名称
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index >= len(self.attrs):
            raise StopIteration

        attr = self.attrs[self.index]
        return attr, getattr(self.obj, attr)

我定义一个ObjAttrIterator类,初始化时传入一个实例对象,通过__init__ 属性获得实例对象的所有属性,一个对象如果实现了__iter__方法就是可迭代对象,如果又同时实现了__next__ 方法,那么它就是迭代器,迭代器的__iter__方法通常返回其自身,__next__ 提供迭代功能。

下面的代码演示如何使用他们

t = Test("小明", 14)
iterator = ObjAttrIterator(t)

for attr, value in iterator:
    print(attr, value)

程序输出结果

name 小明
age 14

目前,ObjAttrIterator迭代器需要单独使用,还不是最方便的方式,在Test类的__iter__ 方法返回ObjAttrIterator 实例,可以让程序更加简洁

class ObjAttrIterator():
    def __init__(self, obj):
        self.obj = obj
        self.attrs = list(obj.__dict__.keys())      # 所有属性名称
        self.index = -1
        
    def __iter__(self):
        return self
        
    def __next__(self):
        self.index += 1
        if self.index >= len(self.attrs):
            raise StopIteration

        attr = self.attrs[self.index]
        return attr, getattr(self.obj, attr)

class Test():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'{self.name} 今年 {self.age}'

    def __iter__(self):
        return ObjAttrIterator(self)

    def print(self):
        pass

t = Test("小明", 14)
for attr, value in t:
    print(attr, value)

Test 实现了__iter__ 方法, 它是一个可迭代对象,__iter__ 方法返回的是一个迭代器,在for循环内部,一直在用next方法遍历这个迭代器,就获取到了对象的所有属性和对应的值。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案