Python-构建一个基本的Python迭代器

发布于 2021-02-02 23:23:03

如何在python中创建一个迭代函数(或迭代器对象)?

关注者
0
被浏览
156
1 个回答
  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

    python中的迭代器对象符合迭代器协议,这基本上意味着它们提供了两种方法:__iter__()__next__()

    • __iter__返回迭代器对象,并在循环开始时隐式调用。

    • __next__()方法返回下一个值,并在每次循环增量时隐式调用。当没有更多值要返回时,此方法将引发StopIteration异常,该异常由循环构造以停止迭代的方式隐式捕获。

    这是一个简单的计数器示例:

    class Counter:
        def __init__(self, low, high):
            self.current = low - 1
            self.high = high
    
        def __iter__(self):
            return self
    
        def __next__(self): # Python 2: def next(self)
            self.current += 1
            if self.current < self.high:
                return self.current
            raise StopIteration
    
    
    for c in Counter(3, 9):
        print(c)
    

    这将打印:

    3
    4
    5
    6
    7
    8
    

    如上一个答案所述,使用生成器编写起来更容易:

    def counter(low, high):
        current = low
        while current < high:
            yield current
            current += 1
    
    for c in counter(3, 9):
        print(c)
    

    打印的输出将相同。在内部,生成器对象支持迭代器协议,并且执行与类Counter大致相似的操作。

    David Mertz的文章IteratorsSimple Generators是很好的介绍。



  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

    有四种方法来构建迭代函数:

    • 创建一个生成器(使用yield关键字)
    • 使用生成器表达式(genexp
    • 创建一个迭代器(定义__iter____next__(或next在Python 2.x中)
    • 创建一个Python可以自己迭代的类(定义__getitem__

    例子:

    # generator
    def uc_gen(text):
        for char in text:
            yield char.upper()
    
    # generator expression
    def uc_genexp(text):
        return (char.upper() for char in text)
    
    # iterator protocol
    class uc_iter():
        def __init__(self, text):
            self.text = text
            self.index = 0
        def __iter__(self):
            return self
        def __next__(self):
            try:
                result = self.text[self.index].upper()
            except IndexError:
                raise StopIteration
            self.index += 1
            return result
    
    # getitem method
    class uc_getitem():
        def __init__(self, text):
            self.text = text
        def __getitem__(self, index):
            result = self.text[index].upper()
            return result
    

    要查看所有四个实际方法:

    for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
        for ch in iterator('abcde'):
            print ch,
        print
    

    结果是:

    A B C D E
    A B C D E
    A B C D E
    A B C D E
    

    注意事项:

    两种生成器类型(uc_gen和uc_genexp)不能为reversed(); 普通的迭代器(uc_iter)需要使用__reversed__magic方法(该方法必须返回一个向后的新迭代器);并且getitem iteratableuc_getitem)必须具有__len__魔术方法:

        # for uc_iter
        def __reversed__(self):
            return reversed(self.text)
    
        # for uc_getitem
        def __len__(self)
            return len(self.text)
    

    为了回答Panic上校关于无限懒惰求值的迭代器的第二个问题,以下是使用上述四种方法中的每一个的示例:

    # generator
    def even_gen():
        result = 0
        while True:
            yield result
            result += 2
    
    
    # generator expression
    def even_genexp():
        return (num for num in even_gen())  # or even_iter or even_getitem
                                            # not much value under these circumstances
    
    # iterator protocol
    class even_iter():
        def __init__(self):
            self.value = 0
        def __iter__(self):
            return self
        def __next__(self):
            next_value = self.value
            self.value += 2
            return next_value
    
    # getitem method
    class even_getitem():
        def __getitem__(self, index):
            return index * 2
    
    import random
    for iterator in even_gen, even_genexp, even_iter, even_getitem:
        limit = random.randint(15, 30)
        count = 0
        for even in iterator():
            print even,
            count += 1
            if count >= limit:
                break
        print
    

    结果(至少在我的示例运行中):

    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
    

    如何选择使用哪一个?这主要是一个品味问题。我最常看到的两种方法是生成器和迭代器协议,以及混合方法(__iter__返回生成器)。

    生成器表达式可用于替换列表推导(它们很懒,因此可以节省资源)。

    如果需要与早期的Python 2.x版本兼容,请使用__getitem__



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看