{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 一、切片"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "取一个list或tuple的部分元素是非常常见的操作。比如，一个list如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "取前3个元素，应该怎么做？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Michael', 'Sarah', 'Tracy']"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 笨办法：\n",
    "[L[0], L[1], L[2]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之所以是笨办法是因为扩展一下，取前N个元素就没辙了。\n",
    "\n",
    "取前N个元素，也就是索引为0-(N-1)的元素，可以用循环："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Michael', 'Sarah', 'Tracy']"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = []\n",
    "n = 3\n",
    "for i in range(n):\n",
    "    r.append(L[i])\n",
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对这种经常取指定索引范围的操作，用循环十分繁琐，因此，Python提供了切片（Slice）操作符，能大大简化这种操作。\n",
    "\n",
    "对应上面的问题，取前3个元素，用一行代码就可以完成切片："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Michael', 'Sarah', 'Tracy']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L[0:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L[0:3]表示，从索引0开始取，直到索引3为止，但不包括索引3。即索引0，1，2，正好是3个元素。\n",
    "\n",
    "如果第一个索引是0，还可以省略："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Michael', 'Sarah', 'Tracy']"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以从索引1开始，取出2个元素出来："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Sarah', 'Tracy']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L[1:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类似的，既然Python支持L[-1]取倒数第一个元素，那么它同样支持倒数切片，试试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Bob', 'Jack']"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L[-2:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "记住倒数第一个元素的索引是-1。\n",
    "\n",
    "切片操作十分有用。我们先创建一个0-99的数列："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "L = list(range(100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 切片操作可以取出某一段数列。比如前10个数：\n",
    "L[:10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 后10个数：\n",
    "L[-10:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 前11-20个数，其下标为0~19：\n",
    "L[10:20]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 2, 4, 6, 8]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 前10个数，每两个取一个：\n",
    "L[:10:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 所有数，每5个取一个：\n",
    "L[::5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 甚至什么都不写，只写[:]就可以原样复制一个list：\n",
    "# L[:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "tuple也是一种list，唯一区别是tuple不可变。因此，tuple也可以用切片操作，只是操作的结果仍是tuple："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 1, 2)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(0, 1, 2, 3, 4, 5)[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字符串'xxx'也可以看成是一种list，每个元素就是一个字符。因此，字符串也可以用切片操作，只是操作结果仍是字符串："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ABC\n"
     ]
    }
   ],
   "source": [
    "s = 'ABCDEFG'[:3]\n",
    "print(s)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "利用切片操作，实现一个trim()函数，去除字符串首尾的空格，注意不要调用str的strip()方法：\n",
    "\n",
    "def trim(s):\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello \n",
      "hello \n",
      "hello\n"
     ]
    }
   ],
   "source": [
    "def trim(s):\n",
    "    n=len(s)\n",
    "    if s[:1]==' ':\n",
    "        s=s[1:n]\n",
    "    elif s[:n-1]==' ':\n",
    "        s=s[:n-1]\n",
    "    else:\n",
    "        s=s\n",
    "    return s\n",
    "\n",
    "#测试用例\n",
    "s1 =' hello '\n",
    "s2 ='hello '\n",
    "s3 =' hello'\n",
    "print(trim(s1))\n",
    "print(trim(s2))\n",
    "print(trim(s3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 二、迭代"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果给定一个list或tuple，我们可以通过for循环来遍历这个list或tuple，这种**遍历**我们称为迭代（Iteration）。\n",
    "\n",
    "在Python中，迭代是通过for ... in来完成的，而很多语言比如C语言，迭代list是通过下标完成的，\n",
    "#### 比如Java代码："
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "for (i=0; i<list.length; i++) {\n",
    "    n = list[i];\n",
    "}\n",
    "\n",
    "for i in list:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python的for循环抽象程度要高于C的for循环，因为Python的for循环不仅可以用在list或tuple上，还可以作用在其他可迭代对象上。\n",
    "\n",
    "list这种数据类型虽然有下标，但很多其他数据类型是没有下标的，但是，**只要是可迭代对象，无论有无下标，都可以迭代**，\n",
    "\n",
    "**比如dict就可以迭代**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a\n",
      "b\n",
      "c\n"
     ]
    }
   ],
   "source": [
    "d = {'a': 1, 'b': 2, 'c': 3}\n",
    "for key in d:\n",
    "    print(key)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因为dict的存储不是按照list的方式顺序排列，所以，迭代出的结果顺序很可能不一样。\n",
    "\n",
    "默认情况下，dict迭代的是key。如果要迭代value，可以用for value in d.values()，如果要同时迭代key和value，可以用for k, v in d.items()。\n",
    "\n",
    "**字符串也是可迭代对象**，因此，也可以作用于for循环："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A\n",
      "B\n",
      "C\n"
     ]
    }
   ],
   "source": [
    "for ch in 'ABC':\n",
    "    print(ch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，当我们使用for循环时，只要作用于一个可迭代对象，for循环就可以正常运行，而我们不太关心该对象究竟是list还是其他数据类型。\n",
    "\n",
    "那么，如何判断一个对象是可迭代对象呢？方法是通过collections模块的Iterable类型判断："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import Iterable\n",
    "isinstance('abc', Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance([1,2,3], Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(123, Iterable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后一个小问题，如果要对list实现类似Java那样的下标循环怎么办？Python内置的enumerate函数可以把一个list变成索引-元素对，这样就可以在for循环中同时迭代索引和元素本身："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(0, 'A'), (1, 'B'), (2, 'C')]"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[i for i in enumerate(['A', 'B', 'C'])]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 A\n",
      "1 B\n",
      "2 C\n"
     ]
    }
   ],
   "source": [
    "for i, value in enumerate(['A', 'B', 'C']):\n",
    "     print(i, value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1\n",
      "2 4\n",
      "3 9\n"
     ]
    }
   ],
   "source": [
    "# 上面的for循环里，同时引用了两个变量，在Python里是很常见的，比如下面的代码：\n",
    "for x, y in [(1, 1), (2, 4), (3, 9)]:\n",
    "     print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 三、列表生成式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表生成式即List Comprehensions，是Python内置的非常简单却强大的可以用来创建list的生成式。\n",
    "\n",
    "举个例子，要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(range(1, 11))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做？方法一是循环："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L = []\n",
    "for x in range(1, 11):\n",
    "    L.append(x * x)\n",
    "L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 循环太繁琐，而列表生成式则可以用一行语句代替循环生成上面的list：\n",
    "[x * x for x in range(1, 11)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "写列表生成式时，把要生成的元素x * x放到前面，后面跟for循环，就可以把list创建出来，十分有用，多写几次，很快就可以熟悉这种语法。\n",
    "\n",
    "for循环后面还可以加上if判断，这样我们就可以筛选出仅偶数的平方："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 16, 36, 64, 100]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[x * x for x in range(1, 11) if x % 2 == 0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还可以使用两层循环，可以生成全排列："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[m + n for m in 'ABC' for n in 'XYZ']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('A', 'X'), ('B', 'Y'), ('C', 'Z')]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(zip('ABC','XYZ'))  # zip是把2个序列元素对应位置组成tupe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['.ipynb_checkpoints',\n",
       " '001赋值机制.ipynb',\n",
       " '001输入输出-数据类型-字符编码.ipynb',\n",
       " '001输出格式化字符串.png',\n",
       " '002List-Tuple1.png',\n",
       " '002List-Tuple2.png',\n",
       " '002list和tuple.ipynb',\n",
       " '003条件语句-循环语句.ipynb',\n",
       " '004使用dict和set.ipynb',\n",
       " '005函数.ipynb',\n",
       " '006高级特性列表推导式-迭代-生成器.ipynb',\n",
       " '007高阶函数map-reduce-匿名函数-闭包-装饰器.ipynb',\n",
       " '008module.ipynb',\n",
       " '009OO.ipynb',\n",
       " '010python-concurrent-main',\n",
       " 'modules_test',\n",
       " 'python变量赋值1.png',\n",
       " 'python变量赋值2.png',\n",
       " 'python变量赋值3.png',\n",
       " 'SeabornCN-master',\n",
       " 'Thumbs.db',\n",
       " '排序',\n",
       " '教材',\n",
       " '第七章numpy',\n",
       " '第八章pandas',\n",
       " '第六章matplotlib']"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 运用列表生成式，可以写出非常简洁的代码。例如，列出当前目录下的所有文件和目录名，可以通过一行代码实现：\n",
    "import os # 导入os模块，模块的概念后面讲到\n",
    "[d for d in os.listdir('.')] # os.listdir可以列出文件和目录"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "for循环其实可以同时使用两个甚至多个变量，比如dict的items()可以同时迭代key和value："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = A\n",
      "y = B\n",
      "z = C\n"
     ]
    }
   ],
   "source": [
    "d = {'x': 'A', 'y': 'B', 'z': 'C' }\n",
    "for k, v in d.items():\n",
    "     print(k, '=', v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因此，列表生成式也可以使用两个变量来生成list："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['x=A', 'y=B', 'z=C']"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = {'x': 'A', 'y': 'B', 'z': 'C' }\n",
    "[k + '=' + v for k, v in d.items()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把一个list中所有的字符串变成小写："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['hello', 'world', 'ibm', 'apple']"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L = ['Hello', 'World', 'IBM', 'Apple']\n",
    "[s.lower() for s in L]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 四、生成器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过列表生成式，我们可以直接创建一个列表。<br/>\n",
    "**受到内存限制，列表容量肯定是有限的**。而且，创建一个包含100万个元素的列表，不仅占用很大的存储空间，如果**我们仅仅需要访问前面几个元素**，那后面绝大多数元素占用的空间都白白浪费了。\n",
    "\n",
    "所以，如果列表元素可以按照某种算法推算出来，那我们**是否可以在循环的过程中不断推算出后续的元素呢？**这样就不必创建完整的list，从而节省大量的空间。\n",
    "\n",
    "**在Python中，这种一边循环一边计算的机制，称为生成器：generator。**\n",
    "\n",
    "**(1)第一种方法**很简单，只要把一个列表生成式的[ ]改成( )，就创建了一个generator："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L = [x * x for x in range(10)]\n",
    "L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object <genexpr> at 0x00000274B3B2D468>"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g = (x * x for x in range(10))\n",
    "g"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建L和g的区别仅在于最外层的[]和()，L是一个list，而g是一个generator。\n",
    "\n",
    "我们可以直接打印出list的每一个元素，但我们怎么打印出generator的每一个元素呢？\n",
    "\n",
    "如果要一个一个打印出来，**可以通过next()函数**获得generator的下一个返回值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    " next(g)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    " next(g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**generator保存的是算法**，每次调用next(g)，就计算出g的下一个元素的值，直到计算到最后一个元素，没有更多的元素时，抛出StopIteration的错误。\n",
    "\n",
    "当然，next(g)一般和for循环使用，因为generator也是可迭代对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<generator object <genexpr> at 0x00000274B1807EB8>\n",
      "0\t 1\t 4\t 9\t 16\t 25\t 36\t 49\t 64\t 81\t "
     ]
    }
   ],
   "source": [
    "g = (x * x for x in range(10))\n",
    "print(g)\n",
    "for n in g:\n",
    "    print(n,end='\\t ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，我们创建了一个generator后，基本上永远不会调用next()，而是通过for循环来迭代它，并且不需要关心**StopIteration**的错误。\n",
    "\n",
    "generator非常强大。如果推算的算法比较复杂，用类似列表生成式的for循环无法实现的时候，还可以用函数来实现。\n",
    "\n",
    "比如，著名的斐波拉契数列（Fibonacci），除第一个和第二个数外，任意一个数都可由前两个数相加得到：\n",
    "\n",
    "1, 1, 2, 3, 5, 8, 13, 21, 34, ...\n",
    "\n",
    "斐波拉契数列用列表生成式写不出来，但是，用函数把它打印出来却很容易："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        print(b)\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return 'done'"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "注意，赋值语句：\n",
    "a, b = b, a + b\n",
    "\n",
    "相当于：\n",
    "t = (b, a + b) # t是一个tuple\n",
    "a = t[0]\n",
    "b = t[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但不必显式写出临时变量t就可以赋值。\n",
    "\n",
    "上面的函数可以输出斐波那契数列的前N个数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'done'"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (2)第二种方法使用生成器得方式 ：使用yield\n",
    "仔细观察，可以看出，fib函数实际上是定义了斐波拉契数列的推算规则，可以从第一个元素开始，推算出后续任意的元素，这种逻辑其实非常类似generator。\n",
    "\n",
    "也就是说，上面的函数和generator仅一步之遥。要把fib函数变成generator，只需要把print(b)改为yield b就可以了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return 'done'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这就是定义generator的另一种方法。**如果一个函数定义中包含yield关键字**，那么这个函数就不再是一个普通函数，而**是一个generator**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object fib at 0x00000274B184A0A0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = fib(6)\n",
    "f"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，最难理解的就是generator和函数的执行流程不一样。函数是顺序执行，遇到return语句或者最后一行函数语句就返回。而变成generator的函数，在每次调用next()的时候执行，遇到yield语句返回，再次执行时从上次返回的yield语句处继续执行。\n",
    "\n",
    "**举个简单的例子，定义一个generator**，依次返回数字1，3，5："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "def odd():\n",
    "    print('step 1')\n",
    "    yield 1\n",
    "    print('step 2')\n",
    "    yield(2)\n",
    "    print('step 3')\n",
    "    yield(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用该generator时，首先要生成一个generator对象，然后用next()函数不断获得下一个返回值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "o = odd()\n",
    "next(o)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "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<ipython-input-56-ac94be31f4f2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mo\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": [
    "next(o)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "可以看到，odd不是普通函数，而是generator，在执行过程中，遇到yield就中断，下次又继续执行。执行3次yield后，已经没有yield可以执行了，所以，第4次调用next(o)就报错。\n",
    "\n",
    "回到fib的例子，我们在循环过程中不断调用yield，就会不断中断。当然要给循环设置一个条件来退出循环，不然就会产生一个无限数列出来。\n",
    "\n",
    "同样的，把函数改成generator后，我们基本上从来不会用next()来获取下一个返回值，而是直接使用for循环来迭代："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<generator object fib at 0x00000274B42B91A8>\n"
     ]
    }
   ],
   "source": [
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b  # 生成器遇到yield会中断，把 b 值返回，然后继续执行\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return 'done'\n",
    "\n",
    "ffb = fib(6)\n",
    "print(ffb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "next(ffb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n"
     ]
    }
   ],
   "source": [
    "for n in fib(6):\n",
    "     print(n)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "但是用for循环调用generator时，发现拿不到generator的return语句的返回值。如果想要拿到返回值，必须捕获StopIteration错误，返回值包含在StopIteration的value中："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "g: 1\n",
      "g: 1\n",
      "g: 2\n",
      "g: 3\n",
      "g: 5\n",
      "g: 8\n",
      "Generator return value: done\n"
     ]
    }
   ],
   "source": [
    "g = fib(6)\n",
    "while True:\n",
    "    try:\n",
    "        x = next(g)\n",
    "        print('g:', x)\n",
    "    except StopIteration as e:\n",
    "        print('Generator return value:', e.value)\n",
    "        break\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 五、迭代器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们已经知道，可以直接作用于for循环的数据类型有以下几种：\n",
    "\n",
    "一类是集合数据类型，如**list、tuple、dict、set、str等**；\n",
    "\n",
    "一类是generator，包括**生成器和带yield的generator function**。\n",
    "\n",
    "这些可以直接作用于for循环的对象统称为可迭代对象：Iterable。\n",
    "\n",
    "可以**使用isinstance()**判断一个对象是否是Iterable对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import Iterable\n",
    "isinstance([], Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance({}, Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance('abc', Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance((x for x in range(10)), Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(100, Iterable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而生成器不但可以作用于for循环，还可以被next()函数不断调用并返回下一个值，直到最后抛出StopIteration错误表示无法继续返回下一个值了。\n",
    "\n",
    "### 迭代器\n",
    "\n",
    "可以被next()函数调用并不断返回下一个值的对象称为**迭代器：Iterator**。\n",
    "\n",
    "可以使用isinstance()判断一个对象是否是Iterator对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import Iterator\n",
    "isinstance((x for x in range(10)), Iterator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance([], Iterator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance({}, Iterator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance('abc', Iterator)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "生成器都是Iterator对象，但list、dict、str虽然是Iterable，却不是Iterator。\n",
    "\n",
    "把list、dict、str等Iterable变成Iterator可以使用iter()函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(iter([]), Iterator)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 字符串，列表或元组对象都可用于创建迭代器："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "list=[1,2,3,4]\n",
    "it = iter(list)    # 创建迭代器对象\n",
    "print (next(it))   # 输出迭代器的下一个元素\n",
    "print (next(it))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 "
     ]
    }
   ],
   "source": [
    "list=[1,2,3,4]\n",
    "it = iter(list)    # 创建迭代器对象\n",
    "for x in it:\n",
    "    print (x, end=\" \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:root:Internal Python error in the inspect module.\n",
      "Below is the traceback from this internal error.\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "Traceback (most recent call last):\n",
      "  File \"<ipython-input-10-c1847631e293>\", line 9, in <module>\n",
      "    print (next(it))\n",
      "StopIteration\n",
      "\n",
      "During handling of the above exception, another exception occurred:\n",
      "\n",
      "Traceback (most recent call last):\n",
      "  File \"d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\site-packages\\IPython\\core\\interactiveshell.py\", line 3331, in run_code\n",
      "    exec(code_obj, self.user_global_ns, self.user_ns)\n",
      "  File \"<ipython-input-10-c1847631e293>\", line 11, in <module>\n",
      "    sys.exit()\n",
      "SystemExit\n",
      "\n",
      "During handling of the above exception, another exception occurred:\n",
      "\n",
      "Traceback (most recent call last):\n",
      "  File \"d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\site-packages\\IPython\\core\\ultratb.py\", line 1148, in get_records\n",
      "    return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)\n",
      "  File \"d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\site-packages\\IPython\\core\\ultratb.py\", line 316, in wrapped\n",
      "    return f(*args, **kwargs)\n",
      "  File \"d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\site-packages\\IPython\\core\\ultratb.py\", line 350, in _fixed_getinnerframes\n",
      "    records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))\n",
      "  File \"d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\inspect.py\", line 1490, in getinnerframes\n",
      "    frameinfo = (tb.tb_frame,) + getframeinfo(tb, context)\n",
      "AttributeError: 'tuple' object has no attribute 'tb_frame'\n"
     ]
    },
    {
     "ename": "SystemExit",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "An exception has occurred, use %tb to see the full traceback.\n",
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mSystemExit\u001b[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\ProgramData\\Anaconda3\\envs\\py36\\lib\\site-packages\\IPython\\core\\interactiveshell.py:3339: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n",
      "  warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
     ]
    }
   ],
   "source": [
    "# 使用next\n",
    "import sys\n",
    " \n",
    "list=[1,2,3,4]\n",
    "it = iter(list)    # 创建迭代器对象\n",
    " \n",
    "while True:\n",
    "    try:\n",
    "        print (next(it))\n",
    "    except StopIteration:\n",
    "        sys.exit()\n",
    "#         pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 创建一个迭代器\n",
    "\n",
    "把一个类作为一个迭代器使用需要在类中实现两个方法 $__iter__()$ 与 $__next__()$ 。\n",
    "\n",
    "如果你已经了解的面向对象编程，就知道类都有一个构造函数，Python 的构造函数为 __init__(), 它会在对象初始化的时候执行。\n",
    "\n",
    "更多内容查阅：Python3 面向对象\n",
    "\n",
    "__iter__() 方法返回一个特殊的迭代器对象， 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。\n",
    "\n",
    "__next__() 方法（Python 2 里是 next()）会返回下一个迭代器对象。\n",
    "\n",
    "创建一个返回数字的迭代器，初始值为 1，逐步递增 1："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "class MyNumbers:\n",
    "    def __iter__(self):\n",
    "        self.a = 1\n",
    "        return self\n",
    " \n",
    "    def __next__(self):\n",
    "        x = self.a\n",
    "        self.a += 1\n",
    "        return x\n",
    " \n",
    "myclass = MyNumbers()\n",
    "myiter = iter(myclass)\n",
    " \n",
    "print(next(myiter))\n",
    "print(next(myiter))\n",
    "print(next(myiter))\n",
    "print(next(myiter))\n",
    "print(next(myiter))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:py36] *",
   "language": "python",
   "name": "conda-env-py36-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
