zoukankan      html  css  js  c++  java
  • python中迭代器(转)

    一、迭代器与for语句

    网上许多文章说Python的for语句中,in关键字后面的对象是一个集合。例如

    for i in [1,2,3]  
        print i 

    上面代码中in关键字后面的对象[1,2,3]是一个list,也是一个集合。

    但in关键字后面的对象其实不必是一个集合。后面接一个序列对象也是合法的。 例如

    myrange = MyRange(0, 10)  
    for i in myrange:  
        print i  

    上面代码中的myrange对象是一个序列对象,但不是集合。参见上一篇博文

    事实上,for语句中in关键字后面的对象也不必是序列对象,它只需要是一个可迭代对象(Iterable)即可。

    一个可迭代的对象需要满足下面两个条件之一:

    • 它实现了__iter__方法。该方法会返回一个迭代器对象。
    • 或者它是一个序列对象。

    注意:如果一个类型实现了__iter__方法,那么在该方法中显式地给出了与该类型相关的迭代器如何构造。可是,对于序列类型来说,它有一个很天然的迭代器。因此,无需通过实现__iter__方法来显式定义。下一篇博文将介绍如何通过序列对象构造天然的迭代器。这里先介绍迭代器对象的严格定义。

    一个迭代器,本质上也是一个序列。它需要实现下面两个方法。

    • next方法(老版本的Python叫__next__方法)。当第11次调用next方法时,会返回序列的第1个元素;当第2次调用next方法时,会返回序列的第2个元素;当序列中的元素耗尽,抛出StopIteration异常。
    • __iter__方法。前面说过__iter__方法通常返回迭代器对象。因此,对于一个迭代器来说,它的__iter__方法只需返回其本身即可。

    通过上面的定义,我们知道,一个迭代器对象,也必是可迭代的。

    下面的代码定义了一个迭代器。

    class MyIterator:  
        def __init__(self, start, end):  
            self.start = start  
            self.end = end  
      
        def next(self):  
            if self.start >= self.end:  
                raise StopIteration  
            self.start = self.start + 1  
            return self.start - 1  
      
        def __iter__(self):  
            return self  

    测试代码

    myiter = MyIterator(0, 2)  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()  

    输出结果如下

    0  
    1  
    Traceback (most recent call last):  
      File "test.py", line 27, in <module>  
        print myiter.next()  
      File "test.py", line 16, in next  
        raise StopIteration  
    StopIteration 

    下面的代码测试在for语句中使用迭代器

    myiter = MyIterator(0, 10)  
    for i in myiter:  
        print i

    输出结果

    0  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9 

    二、iter方法

    iter方法是Python的一个内建方法,它会返回一个迭代器对象。它定义如下

    iter(o[, sentinel])

    第一个参数o可以是一个可迭代对象,也可以是一个可调用对象。

    当参数o是可迭代对象时,第二个参数可省。这里又分为两种情况。

    • 如果参数o实现了__iter__方法,则直接调用该方法,创建迭代器。
    • 如果参数o没有实现__iter__方法,那么它比是一个序列对象。iter方法根据该序列对象诱导出一个迭代器。

    如果参数o是一个可调用对象时,iter方法返回的迭代器工作原理如下。

    每次调用迭代器的next方法时,最终都会调用o方法。此时第2个参数sentinel必须给定。当o方法的返回值与sentinel相同时,抛出StopIteration异常。

    一些例子

    1. 由集合构造迭代器

    myiter = iter([1,2,3])  
    myiter.next()  
    myiter.next()  
    myiter.next()  
    myiter.next()

    输出结果

    1  
    2  
    3  
    Traceback (most recent call last):  
      File "test.py", line 27, in <module>  
        print myiter.next()  
    StopIteration 

    2. 由序列对象构造迭代器。

    myiter = iter(MyRange(1, 4))  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()

    MyRange的定义见博文

    3. 由迭代器对象构造迭代器。

    myiter1 = MyIterator(1, 4)  
    myiter2 = iter(myiter1)  
    print myiter1 == myiter2  

    MyIterator的定义见博文。上面代码输出的值为True。

    4. 由可调用对象构造迭代器。

    myiter1 = MyIterator(1, 4)  
    myiter2 = iter(myiter1.next, 4)  
      
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()  

    输出结果

    1  
    2  
    3  
    Traceback (most recent call last):  
      File "test.py", line 27, in <module>  
        print myiter.next()  
    StopIteration 

    有兴趣的读者可以尝试下面代码的输出结果。

    myiter2 = iter(myiter1.next, 3)  
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()  
    myiter2 = iter(myiter1.next, 5)  
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()  
    print myiter2.next()

    三、for循环工作原理

    对于如下的for语句

    for obj in iterable_obj:  
       do something with obj  

    首先会调用iter方法获取关于iterable_obj对象的迭代器,然后不断调用迭代器对象的next方法,直至抛出异常位置。

    为了说明这一点,看下面的例子。

    myiter = MyIterator(1, 4)  
    for i in myiter:  
        pass  
      
    print myiter.next()

    上面代码会抛出StopIteration异常。因为在for语句阶段会将myiter迭代器对象的元素全部读完。MyIterator类的定义参见博文

    到目前位置,我们有三种方式表示一个序列。它们是集合,序列对象和迭代器对象。

    • 集合(如列表,元组,字符串等)中的元素可读可写。当然不同的集合类型,可写的权限有所不同。例如列表中的元素可以随意添加,删除,修改。而元组中的元素虽然不能添加、删除和修改,但是我们可以对两个元组对象进行连接。当然,连接后会创建新的元组对象。
    • 序列对象中的元素可读但不可写。在基本的定义中,两个序列对象也不必支持元组对象那样的连接操作。这样的好处是,在有的情况下,我们不必开辟专门的内存空间保存序列对象中的所有元素。
    • 迭代器对象中的元素也是可读不可写的。但是,迭代器对读的权限做了一个限制,就是只能从头至尾读一遍。迭代器对象的优势在于它有的时候比序列对象更加灵活。通常情况下,序列对象中的元素顺序是不能变的。而对于迭代器对象来说,我们只需要指定不同的next方法,就能实现不同的读取顺序。一个比较经典的例子,就是我们可以实现一个迭代器类,同时支持树的深度优先和广度优先遍历。显然用迭代器实现这个功能比用序列来实现要简单许多。

    四、序列诱导迭代器

    如果我们自己要实现iter方法,通过一个序列对象构造出一个迭代器,会怎么做呢?

    首先需要定义一个迭代器类。

    class MyIteratorFromSequence:  
        def __init__(self, sequence):  
            self.start = 0  
            self.sequence = sequence  
      
        def next(self):  
            if self.start >= len(self.sequence):  
                raise StopIteration  
            self.start = self.start + 1  
            return self.sequence[self.start - 1]  
      
        def __iter__(self):  
            return self  

    上面的代码和博文中MyIterator类的定义完全类似,所不同的是,每次执行next方法,返回的不再是start - 1,而是序列对象的第start - 1个元素。

    测试这个迭代器的运行效果

    mylist = [1, 3, 5, 'a', 'b', 'c']  
    myiter = MyIteratorFromSequence(mylist)  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()  
    print myiter.next()  
    print myiter.next() 

    输出结果为

    1  
    3  
    5  
    a  
    b  
    c

    有了MyIterorFromSequence的定义,iter方法的实现就很简单了。

    def myiter(sequence):  
        return MyIteratorFromSequence(sequence)  

    注意:上面的代码实现的功能比内置的iter方法要简单许多。因为我们只实现了序列对象到迭代器对象的构造。

    有了上面的分析,下面代码的输出就在情理之中了。

    mylist = [1, 2, 3]  
    myiter = iter(mylist)  
    mylist.append(4)  
      
    for i in myiter:  
        print i  

    输出结果为

    1  
    2  
    3  
    4 

    转自:http://blog.csdn.net/hedan2013/article/details/55519047

  • 相关阅读:
    [LC] 347. Top K Frequent Elements
    [LC] 659. Split Array into Consecutive Subsequences
    [LC] 430. Flatten a Multilevel Doubly Linked List
    [LC] 271. Encode and Decode Strings
    [LC] 373. Find K Pairs with Smallest Sums
    [LC] 1048. Longest String Chain
    [LC] 297. Serialize and Deserialize Binary Tree
    ubuntu 创建 PyCharm 桌面快捷方式 (或者叫 启动器 )
    scala private 和 private[this] 的区别
    %s %r 区别 转
  • 原文地址:https://www.cnblogs.com/shixisheng/p/7090680.html
Copyright © 2011-2022 走看看