{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 迭代器" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 简介" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "迭代器对象可以在 `for` 循环中使用:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "4\n", "6\n" ] } ], "source": [ "x = [2, 4, 6]\n", "\n", "for n in x:\n", " print n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "其好处是不需要对下标进行迭代,但是有些情况下,我们既希望获得下标,也希望获得对应的值,那么可以将迭代器传给 `enumerate` 函数,这样每次迭代都会返回一组 `(index, value)` 组成的元组:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pos 0 is 2\n", "pos 1 is 4\n", "pos 2 is 6\n" ] } ], "source": [ "x = [2, 4, 6]\n", "\n", "for i, n in enumerate(x):\n", " print 'pos', i, 'is', n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "迭代器对象必须实现 `__iter__` 方法:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "x = [2, 4, 6]\n", "i = x.__iter__()\n", "print i" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`__iter__()` 返回的对象支持 `next` 方法,返回迭代器中的下一个元素:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "print i.next()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当下一个元素不存在时,会 `raise` 一个 `StopIteration` 错误:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n", "6\n" ] } ], "source": [ "print i.next()\n", "print i.next()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "ename": "StopIteration", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mi\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mStopIteration\u001b[0m: " ] } ], "source": [ "i.next()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "很多标准库函数返回的是迭代器:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "r = reversed(x)\n", "print r" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "调用它的 `next()` 方法:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6\n", "4\n", "2\n" ] } ], "source": [ "print r.next()\n", "print r.next()\n", "print r.next()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "字典对象的 `iterkeys, itervalues, iteritems` 方法返回的都是迭代器:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "x = {'a':1, 'b':2, 'c':3}\n", "i = x.iteritems()\n", "print i" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "迭代器的 `__iter__` 方法返回它本身:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print i.__iter__()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('a', 1)\n" ] } ], "source": [ "print i.next()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 自定义迭代器" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "自定义一个 list 的取反迭代器:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class ReverseListIterator(object):\n", " \n", " def __init__(self, list):\n", " self.list = list\n", " self.index = len(list)\n", " \n", " def __iter__(self):\n", " return self\n", " \n", " def next(self):\n", " self.index -= 1\n", " if self.index >= 0:\n", " return self.list[self.index]\n", " else:\n", " raise StopIteration" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9 8 7 6 5 4 3 2 1 0\n" ] } ], "source": [ "x = range(10)\n", "for i in ReverseListIterator(x):\n", " print i," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "只要我们定义了这三个方法,我们可以返回任意迭代值:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Collatz(object):\n", " \n", " def __init__(self, start):\n", " self.value = start\n", " \n", " def __iter__(self):\n", " return self\n", " \n", " def next(self):\n", " if self.value == 1:\n", " raise StopIteration\n", " elif self.value % 2 == 0:\n", " self.value = self.value / 2\n", " else:\n", " self.value = 3 * self.value + 1\n", " return self.value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这里我们实现 [Collatz 猜想](http://baike.baidu.com/view/736196.htm):\n", "\n", "- 奇数 n:返回 3n + 1\n", "- 偶数 n:返回 n / 2\n", "\n", "直到 n 为 1 为止:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1\n" ] } ], "source": [ "for x in Collatz(7):\n", " print x," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "不过迭代器对象存在状态,会出现这样的问题:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "22 11\n", "34 17\n", "52 26\n", "13 40\n", "20 10\n", "5 16\n", "8 4\n", "2 1\n" ] } ], "source": [ "i = Collatz(7)\n", "for x, y in zip(i, i):\n", " print x, y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一个比较好的解决方法是将迭代器和可迭代对象分开处理,这里提供了一个二分树的中序遍历实现:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class BinaryTree(object):\n", " def __init__(self, value, left=None, right=None):\n", " self.value = value\n", " self.left = left\n", " self.right = right\n", "\n", " def __iter__(self):\n", " return InorderIterator(self)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class InorderIterator(object):\n", " \n", " def __init__(self, node):\n", " self.node = node\n", " self.stack = []\n", " \n", " def next(self):\n", " if len(self.stack) > 0 or self.node is not None:\n", " while self.node is not None:\n", " self.stack.append(self.node)\n", " self.node = self.node.left\n", " node = self.stack.pop()\n", " self.node = node.right\n", " return node.value\n", " else:\n", " raise StopIteration()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": true }, "outputs": [], "source": [ "tree = BinaryTree(\n", " left=BinaryTree(\n", " left=BinaryTree(1),\n", " value=2,\n", " right=BinaryTree(\n", " left=BinaryTree(3),\n", " value=4,\n", " right=BinaryTree(5)\n", " ),\n", " ),\n", " value=6,\n", " right=BinaryTree(\n", " value=7,\n", " right=BinaryTree(8)\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3 4 5 6 7 8\n" ] } ], "source": [ "for value in tree:\n", " print value," ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "不会出现之前的问题:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 1\n", "2 2\n", "3 3\n", "4 4\n", "5 5\n", "6 6\n", "7 7\n", "8 8\n" ] } ], "source": [ "for x,y in zip(tree, tree):\n", " print x, y" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }