zoukankan      html  css  js  c++  java
  • 迭代器

    迭代
      可以使用hasattr函数来检测一个对象是否是可迭代的,即检查对象中是否有__iter__方法。

    >>> hasattr(list, '__iter__')    # 列表可迭代
    True
    >>> hasattr(int, '__iter__')    # 整型不可迭代
    False

       __iter__是一个特殊方法,它是迭代规则的基础,有了它,就说明对象是可迭代的。
        跟迭代有关的一个内建函数iter(),它返回一个迭代器对象。

    >>> lst = list('python')    # lst是可迭代的
    >>> hasattr(lst, '__iter__')
    True
    >>> hasattr(lst, '__next__')    # lst中没有__next__方法,不是迭代器对象
    False
    >>> iter_lst = iter(lst)    # 由lst产生一个迭代器对象iter_lst,等同于iter_lst = lst.__iter__()
    >>> hasattr(iter_lst, '__iter__')
    True
    >>> hasattr(iter_lst, '__next__')    # iter_lst中有__next__属性,是迭代器对象
    True

      可迭代的对象中有__iter__方法,但不一定是迭代器对象;迭代器对象一定是可迭代的,它同时拥有__iter__方法和__next__方法。
      迭代器对象要遵循迭代协议,即实现了__next__方法。我们可以直接在类中定义__next__方法以实现迭代器协议,也可以通过执行对象名.__iter__()的方式实现迭代器,还可以通过执行python的内建函数iter(),即iter(对象名)的方式实现迭代器对象。

    迭代器
      迭代器可以用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的__next__方法(Python3中是对象的__next__方法,Python2中是对象的next()方法)。
      所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身self即可。
      编写一个迭代器对象。

    >>> class MyRange:
    ...     def __init__(self, n):
    ...         self.i = 1
    ...         self.n = n
    ...     def __iter__(self):    # 实现__iter__方法
    ...         return self
    ...     def __next__(self):    # 实现__next__方法
    ...         if self.i <= self.n:
    ...             i = self.i
    ...             self.i += 1
    ...             return i
    ...         else:
    ...             raise StopIteration    # 条件结束,必须返回StopIteration异常
    ... 
    >>> x = MyRange(7)
    >>> print([i for i in x])
    [1, 2, 3, 4, 5, 6, 7]
    >>> print(hasattr(x, '__iter__'))    # 自定义的迭代器对象中有__iter__方法和__next__方法
    True
    >>> print(hasattr(x, '__next__'))
    True

      以上代码是仿写类似range()的类,但它跟range()又有所不同,它是一个迭代器对象,有以下特点:
      __iter__()方法是类中的核心,它返回了迭代器本身。一个实现了__iter__方法的对象,就意味着它是可迭代的。
      实现了__next__()方法,使得这个对象是迭代器对象。可迭代的对象不一定是迭代器对象,如列表、元组等;迭代器对象一定可迭代。
      再实现一个斐波那契数列的迭代器对象。利用迭代器对象实现斐波那契数列的优点是节省内存,迭代器对象的斐波那契数列不会将数列的所有值都存放在内存中,只会一个一个地取出,而且取出了后一个,前一个值不会保存在内存值。

    >>> class Fibs:
    ...     def __init__(self, max_num):
    ...         self.max_num = max_num
    ...         self.a = 0
    ...         self.b = 1
    ...     def __iter__(self):
    ...         return self
    ...     def __next__(self):
    ...         fib = self.a
    ...         if fib > self.max_num:
    ...             raise StopIteration
    ...         self.a, self.b = self.b, self.a + self.b
    ...         return fib
    ... 
    >>> fibs = Fibs(7)
    >>> print(list(fibs))
    [0, 1, 1, 2, 3, 5]

      结合上面的斐波那契数列的迭代器对象,对迭代器做概括:
        1.在python中,迭代器是遵循迭代协议的对象。
        2.可以使用iter()函数从任何序列得到迭代器(如list、tuple、dict等)。
        3.自己编写迭代器对象,即编写类,其中实现__iter__()和__next__()方法。当没有元素时,引发StopIteration异常。
        4.如果有很多值,列表会占用太多的内存,而迭代器则占用更少内存,因为__next__机制,执行一次给一个值,内存当中只会保存当前__next__给定的值,前面的值不会被保存,所以节省内存空间。
        5.迭代器从第一个元素开始访问,直到所有元素被访问完结束,只能往前,不能后退。且迭代器中的元素是一次性的,只能被取出一次。

    >>> lst = [x for x in range(7)]    # 列表解析式的结果依然是列表对象
    >>> lst
    [0, 1, 2, 3, 4, 5, 6]
    >>> lst    # 可以无数次被调用
    [0, 1, 2, 3, 4, 5, 6]
    >>> tu = (x for x in range(9))    # 元组解析式是一个迭代器对象
    >>> tu
    <generator object <genexpr> at 0x10218a1b0>
    >>> list(tu)    # 迭代器对象中的内容只能被调用一次,是一次性的
    [0, 1, 2, 3, 4, 5, 6, 7, 8]
    >>> list(tu)    # 迭代器对象只能向前,不能后退
    []
    >>> tu2 = (x for x in range(3))
    >>> tu2.__next__()    # 迭代器对象就是通过调用它的__next__方法来取值
    0
    >>> tu2.__next__()
    1
    >>> tu2.__next__()
    2
    >>> tu2.__next__()    # 没有元素则抛出StopIteration异常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    >>> tu3 = (x for x in range(3))
    >>> next(tu3)    # 通过内置函数来取出迭代器中的元素
    0
    >>> next(tu3)    # next(tu3)就相当于执行tu3.__next__()
    1
    >>> next(tu3)    # 内置函数next()就是在执行迭代器中的__next__方法
    2
    >>> next(tu3)    # 没有元素则抛出StopIteration异常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    >>> next(tu3, ‘aa’)    # next()函数可提供一个默认值,没有元素时输出默认值
    aa

    for循环和迭代器:
      对一个可迭代对象执行for循环遍历,首先for循环会对对象调用iter()函数,iter()函数会返回一个定义了__next__方法的迭代器对象,再通过对对象调用next()函数(next()函数会执行对象内部的__next__方法)来逐个访问对象的内容。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。

    >>> l = list('python')    # 可迭代的列表对象,对象中有__iter__方法
    >>> for i in l:    # 执行for循环,会对对象l调用iter()函数,返回一个迭代器对象
    ...     print(i, end=' ')    # 然后再对迭代器对象调用next()函数取值
    ... 
    p y t h o n
  • 相关阅读:
    java 笔记(2) 接口作为引用数据类型
    linux 笔记(5)让vi或vim显示行数和不显示行数
    linux 笔记(4)Ubuntu 使用时vi编辑器时,不能使用backspace键来进行退格或者不能正常使用
    linux 笔记(3)sudo passwd 设置root用户的密码
    matlab笔记(1) 元胞结构cell2mat和num2cell
    linux 笔记(2) 目录直接强行删除rm -rf *(删除当前目录所有的内容)
    linux 笔记(1) ctrl+c,ctrl+z,ctrl+d
    C51单片机项目:红绿灯
    C51单片机项目:时钟
    java 笔记(1)在子类中,一定要访问父类的有参构造方法?
  • 原文地址:https://www.cnblogs.com/wgbo/p/9953360.html
Copyright © 2011-2022 走看看