python 迭代器,生成器

在 python 中咱們經常使用 for in 來遍歷 list, set, dict, str 等。
for in 的本質就幹了兩件事:python

  1. 調用 __iter__() 獲取迭代器;
  2. 調用 next() 直到 StopIteration 異常; (python3 中是 __next__())

迭代器

咱們先了解幾個概念:算法

  • Iterable: 可迭代對象
  • Iterator: 迭代器

咱們先看看 Iterable 的實現ssh

from collections import Iterable

help(Iterable)

class Iterable(__builtin__.object)
 |  Methods defined here:
 |
 |  __iter__(self)
 |
 |  ----------------------------------------------------------------------
 |  Class methods defined here:
 |
 |  __subclasshook__(cls, C) from abc.ABCMeta
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |
 |  __abstractmethods__ = frozenset(['__iter__'])
 |
 |  __metaclass__ = <class 'abc.ABCMeta'>
 |      Metaclass for defining Abstract Base Classes (ABCs).
 |
 |      Use this metaclass to create an ABC.  An ABC can be subclassed
 |      directly, and then acts as a mix-in class.  You can also register
 |      unrelated concrete classes (even built-in classes) and unrelated
 |      ABCs as 'virtual subclasses' -- these and their descendants will

再看看 Iterator 的實現ide

from collections import Iterator

help(Iterator)

class Iterator(Iterable)
 |  Method resolution order:
 |      Iterator
 |      Iterable
 |      __builtin__.object
 |
 |  Methods defined here:
 |
 |  __iter__(self)
 |
 |  next(self)
 |      Return the next item from the iterator. When exhausted, raise StopIteration
 |
 |  ----------------------------------------------------------------------
 |  Class methods defined here:
 |
 |  __subclasshook__(cls, C) from abc.ABCMeta
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |
 |  __abstractmethods__ = frozenset(['next'])
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Iterable:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from Iterable:
 |
 |  __metaclass__ = <class 'abc.ABCMeta'>
 |      Metaclass for defining Abstract Base Classes (ABCs).
 |
 |      Use this metaclass to create an ABC.  An ABC can be subclassed
 |      directly, and then acts as a mix-in class.  You can also register
 |      unrelated concrete classes (even built-in classes) and unrelated
 |      ABCs as 'virtual subclasses' -- these and their descendants will
 |      be considered subclasses of the registering ABC by the built-in
 |      issubclass() function, but the registering ABC won't show up in
 |      their MRO (Method Resolution Order) nor will method
 |      implementations defined by the registering ABC be callable (not
 |      even via super()).

從繼承關係來看,全部的 Iterator(迭代器)都是 Iterable(可迭代對象),
從實現角度看 Iterator 新增了 next() 方法。ui

判斷是 Iterator 仍是 Iterable

  • 凡是能夠 for 循環的,都是 Iterable;
  • 凡是能夠 next() 的,都是 Iterator;
  • list, tuple, dict, str, set 都不是 Iterator,可是能夠經過 __iter__() 返回一個 Iterator 對象
from collections import Iterator, Iterable

isinstance([1,], Iterator)    // False
isinstance((1,), Iterator)    // False
isinstance({}, Iterator)      // False
isinstance("abc", Iterator)   // False
isinstance(set([]), Iterator) // False

isinstance([1,], Iterable)    // True
isinstance((1,), Iterable)    // True
isinstance({}, Iterable)      // True
isinstance("abc", Iterable)   // True
isinstance(set([]), Iterable) // True

dir([])                       // 沒有 next() 方法
dir([].__iter__())            // 有 next() 方法

生成器

將完了迭代器,咱們再說說生成器,這裏引用廖雪峯博客裏的介紹:this

經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。
並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,
若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。code

因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?
這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,
稱爲生成器(Generator)。協程

生成器的建立很簡單,能夠經過推導列表建立:對象

g = (x * x for x in range(10))  // 使用 [] 返回的是 list, () 返回的是 generator

還有一種方式是經過 yield 關鍵字生成。繼承

先看看生成器的實現:

<genexpr> = class generator(object)
 |  Methods defined here:
 |
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |
 |  close(...)
 |      close() -> raise GeneratorExit inside generator.
 |
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |
 |  send(...)
 |      send(arg) -> send 'arg' into generator,
 |      return next yielded value or raise StopIteration.
 |
 |  throw(...)
 |      throw(typ[,val[,tb]]) -> raise exception in generator,
 |      return next yielded value or raise StopIteration.
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  gi_code
 |
 |  gi_frame
 |
 |  gi_running

能夠發現生成器較迭代器多了 send, throw 等方法。

send

這裏重點介紹下 send 方法,咱們知道在使用迭代器時,遇到 yield 關鍵字
會退出來,下一迭代時會繼續執行。先看個例子:

def MyGenerator():
    value = yield 1
    value = yield value

gen = MyGenerator()
print gen.next()         // print 1
print gen.next()         // print None

咱們看看具體執行過程:

  • 調用 next() 方法,走到 yield 1 退出,注意這個時候尚未走到 value 的 賦值操做(即: value = yield 1 只執行了右側部分)
  • 調用 next() 方法,繼續上次的代碼執行(即:value = yield 1 只執行了右側的賦值部分)
  • 因爲 yield 並無返回值,因此 value = None
  • 返回 None, 並打印

修改下上面的例子:

def MyGenerator():
    value = yield 1
    value = yield value

gen = MyGenerator()
print gen.next()         // print 1
print gen.send(2)        // print 2

send 方法是指定的是上一次被掛起的yield語句的返回值,這麼說有點抽象,咱們看執行過程:

  • 調用 next() 方法,走到 yield 1 退出,注意這個時候尚未走到 value 的 賦值操做(即: value = yield 1 只執行了右側部分)
  • 調用 send(2) 方法,繼續上次的代碼執行(即:value = yield 1 只執行了右側的賦值部分)
  • value 使用 send 傳的值,即: value = 2
  • 返回 2, 並打印

協程

協程就是利用 yield 和生成器的 send() 方法實現的。

相關文章
相關標籤/搜索