{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1.2 Python 进阶"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.1 列表推导式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "循环可以用来生成列表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [x for x in range(4)]\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在循环的过程中使用 `if`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [x for x in range(3,10) if x % 2 == 0]\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2 个`for` 循环:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [(x,y) for x in range(1,3) for y in range(1,3)]\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3 个 `for` 循环："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [(x,y,z) for x in range(1,3) for y in range(1,3) for z in range(1,3)]\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以使用推导式生成集合和字典：  \n",
    "**字典推导式**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "values = [10, 21, 4, 7, 12]\n",
    "square_dict = {x: x**2 for x in values if x <= 10}\n",
    "print(square_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**集合推导式**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "values = [10, 21, 4, 7, 12]\n",
    "square_set = {x**2 for x in values if x <= 10}\n",
    "print(square_set)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.2 函数\n",
    "\n",
    "### 定义函数\n",
    "\n",
    "函数`function`，通常接收输入参数，并有返回值。\n",
    "\n",
    "它负责完成某项特定任务，而且相较于其他代码，具备相对的独立性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, y):\n",
    "    \"\"\"Add two numbers\"\"\"\n",
    "    a = x + y\n",
    "    return a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数通常有一下几个特征：\n",
    "- 使用 `def` 关键词来定义一个函数。\n",
    "-  `def` 后面是函数的名称，括号中是函数的参数，不同的参数用 `,` 隔开， `def foo():` 的形式是必须要有的，参数可以为空；\n",
    "- 使用缩进来划分函数的内容；\n",
    "-  `docstring` 用 `\"\"\"` 包含的字符串，用来解释函数的用途，可省略；\n",
    "-  `return` 返回特定的值，如果省略，返回 `None` 。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用函数\n",
    "\n",
    "使用函数时，只需要将参数换成特定的值传给函数。\n",
    "\n",
    "**Python** 并没有限定参数的类型，因此可以使用不同的参数类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(2, 3))\n",
    "print(add('foo', 'bar'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，如果传入的两个参数不可以相加，那么 **Python** 会将报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(2, \"foo\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果传入的参数数目与实际不符合，也会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(1, 2, 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "传入参数时，**Python** 提供了两种选项，第一种是上面使用的按照位置传入参数，另一种则是使用关键词模式，显式地指定参数的值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(x=2, y=3))\n",
    "print(add(y=\"foo\", x=\"bar\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以混合这两种模式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(2, y=3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 设定参数默认值\n",
    "\n",
    "可以在函数定义的时候给参数设定默认值，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def quad(x, a=1, b=0, c=0):\n",
    "    return a*x**2 + b*x + c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以省略有默认值的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(quad(2.0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以修改参数的默认值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(quad(2.0, b=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(quad(2.0, 2, c=4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里混合了位置和指定两种参数传入方式，第二个 2 是传给 `a` 的。\n",
    "\n",
    "注意，在使用混合语法时，要注意不能给同一个值赋值多次，否则会报错，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(quad(2.0, 2, a=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 接收不定长参数\n",
    "\n",
    "使用如下方法，可以使函数接受不定数目的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, *args):\n",
    "    total = x\n",
    "    for arg in args:\n",
    "        total += arg\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，`*args` 表示参数数目不定，可以看成一个元组，把第一个参数后面的参数当作元组中的元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(1, 2, 3, 4))\n",
    "print(add(1, 2))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样定义的函数不能使用关键词传入参数，要使用关键词，可以这样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, **kwargs):\n",
    "    total = x\n",
    "    for arg, value in kwargs.items():\n",
    "        print(\"adding %s=%s\"%(arg,value))\n",
    "        total += value\n",
    "    return total\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里， `**kwargs` 表示参数数目不定，相当于一个字典，键和值对应于键值对。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add(10, y=11, z=12, w=13))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "再看这个例子，可以接收任意数目的位置参数和键值对参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def foo(*args, **kwargs):\n",
    "    print(args, kwargs)\n",
    "\n",
    "foo(2, 3, x='bar', z=10)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不过要按顺序传入参数，先传入位置参数 `args` ，再传入关键词参数 `kwargs` 。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 返回多个值\n",
    "\n",
    "函数可以返回多个值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def divid(a, b):\n",
    "    \"\"\"\n",
    "    除法\n",
    "    :param a: number 被除数\n",
    "    :param b: number 除数\n",
    "    :return: 商和余数\n",
    "    \"\"\"\n",
    "    quotient = a // b\n",
    "    remainder = a % b\n",
    "    return quotient, remainder\n",
    "\n",
    "quotient, remainder = divid(7,4)\n",
    "print(quotient, remainder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "事实上，**Python** 将返回的两个值变成了元组："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(divid(7,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因为这个元组中有两个值，所以可以使用\n",
    "\n",
    "    quotient, remainder = divid(7,4)\n",
    "\n",
    "给两个值赋值。\n",
    "\n",
    "列表也有相似的功能："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a, b, c = [1, 2, 3]\n",
    "print(a, b, c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "事实上，不仅仅返回值可以用元组表示，也可以将参数用元组以这种方式传入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def divid(a, b):\n",
    "    \"\"\"\n",
    "    除法\n",
    "    :param a: number 被除数\n",
    "    :param b: number 除数\n",
    "    :return: 商和余数\n",
    "    \"\"\"\n",
    "    quotient = a // b\n",
    "    remainder = a % b\n",
    "    return quotient, remainder\n",
    "\n",
    "z = (7,4)\n",
    "print(divid(*z))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里的`*`必不可少。\n",
    "\n",
    "事实上，还可以通过字典传入参数来执行函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def divid(a, b):\n",
    "    \"\"\"\n",
    "    除法\n",
    "    :param a: number 被除数\n",
    "    :param b: number 除数\n",
    "    :return: 商和余数\n",
    "    \"\"\"\n",
    "    quotient = a // b\n",
    "    remainder = a % b\n",
    "    return quotient, remainder\n",
    "\n",
    "z = {'a':7,'b':4}\n",
    "print(divid(**z))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  `map` 方法生成序列"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其用法为：\n",
    "    \n",
    "    map(aFun, aSeq)\n",
    "\n",
    "将函数 `aFun` 应用到序列 `aSeq` 上的每一个元素上，返回一个列表，不管这个序列原来是什么类型。\n",
    "\n",
    "事实上，根据函数参数的多少，`map` 可以接受多组序列，将其对应的元素作为参数传入函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def divid(a, b):\n",
    "    \"\"\"\n",
    "    除法\n",
    "    :param a: number 被除数\n",
    "    :param b: number 除数\n",
    "    :return: 商和余数\n",
    "    \"\"\"\n",
    "    quotient = a // b\n",
    "    remainder = a % b\n",
    "    return quotient, remainder\n",
    "\n",
    "a = (10, 6, 7)\n",
    "b = [2, 5, 3]\n",
    "print(list(map(divid,a,b)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.3 模块和包"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模块\n",
    "\n",
    "**Python** 会将所有 `.py` 结尾的文件认定为 **Python** 代码文件，考虑下面的脚本 `ex1.py` ："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile ex1.py\n",
    "\n",
    "PI = 3.1416\n",
    "\n",
    "def sum(lst):\n",
    "    \"\"\"\n",
    "    计算 lst 序列所有元素的和\n",
    "    :param lst: 序列 e.g. [1,2,3]\n",
    "    :return: lst 序列所有元素的总和\n",
    "    \"\"\"\n",
    "    \n",
    "    # 获取 lst序列第一个元素\n",
    "    tot = lst[0]\n",
    "    \n",
    "    # 循环遍历 lst 序列剩余元素\n",
    "    for value in lst[1:]:\n",
    "        tot = tot + value\n",
    "    return tot\n",
    "\n",
    "w = [0, 1, 2, 3]\n",
    "print(sum(w), PI)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以执行它："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ex1.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个脚本可以当作一个模块，可以使用`import`关键词加载并执行它（这里要求`ex1.py`在当前工作目录）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ex1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ex1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在导入时，**Python** 会执行一遍模块中的所有内容。\n",
    "\n",
    "`ex1.py` 中所有的变量都被载入了当前环境中，不过要使用\n",
    "\n",
    "    ex1.变量名\n",
    "\n",
    "的方法来查看或者修改这些变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(ex1.PI)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ex1.PI = 3.141592653\n",
    "print(ex1.PI)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还可以用\n",
    "\n",
    "    ex1.函数名\n",
    "\n",
    "调用模块里面的函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(ex1.sum([2, 3, 4]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了提高效率，**Python** 只会载入模块一次，已经载入的模块再次载入时，**Python** 并不会真正执行载入操作，哪怕模块的内容已经改变。\n",
    "\n",
    "例如，这里重新导入 `ex1` 时，并不会执行 `ex1.py` 中的 `print` 语句："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ex1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要重新导入模块时，可以使用 `reload` 强制重新载入它，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from imp import reload\n",
    "reload(ex1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "删除之前生成的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('ex1.py')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `__name__` 属性\n",
    "\n",
    "有时候我们想将一个 `.py` 文件既当作脚本，又能当作模块用，这个时候可以使用 `__name__` 这个属性。\n",
    "\n",
    "只有当文件被当作脚本执行的时候， `__name__`的值才会是 `'__main__'`，所以我们可以：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile ex2.py\n",
    "\n",
    "PI = 3.1416\n",
    "\n",
    "def sum(lst):\n",
    "    \"\"\" Sum the values in a list\n",
    "    \"\"\"\n",
    "    tot = 0\n",
    "    for value in lst:\n",
    "        tot = tot + value\n",
    "    return tot\n",
    "\n",
    "def add(x, y):\n",
    "    \" Add two values.\"\n",
    "    a = x + y\n",
    "    return a\n",
    "\n",
    "def test():\n",
    "    w = [0,1,2,3]\n",
    "    assert(sum(w) == 6)\n",
    "    print('test passed.')\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    test()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ex2.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当作模块导入， `test()` 不会执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ex2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是可以使用其中的变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ex2.PI\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "引入模块时可以为它设置一个别名让使用更方便："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ex2 as e2\n",
    "e2.PI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 其它导入方法\n",
    "\n",
    "可以从模块中导入变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ex2 import add, PI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 `from` 后，可以直接使用 `add` ， `PI`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者使用 `*` 导入所有变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ex2 import *\n",
    "add(3, 4.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种导入方法不是很提倡，因为如果你不确定导入的都有哪些，可能覆盖一些已有的函数。\n",
    "\n",
    "删除文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('ex2.py')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 包\n",
    "\n",
    "假设我们有这样的一个文件夹：\n",
    "\n",
    "foo/\n",
    "- `__init__.py` \n",
    "- `bar.py` (defines func)\n",
    "- `baz.py` (defines zap)\n",
    "\n",
    "这意味着 `foo` 是一个包，我们可以这样导入其中的内容：\n",
    "\n",
    "```python  \n",
    "\n",
    "from foo.bar import func\n",
    "from foo.baz import zap\n",
    "\n",
    "```\n",
    "\n",
    "`bar` 和 `baz` 都是 `foo` 文件夹下的 `.py` 文件。\n",
    "\n",
    "导入包要求：\n",
    "- 文件夹 `foo` 在 **Python** 的搜索路径中\n",
    "- `__init__.py` 表示 `foo` 是一个包，它可以是个空文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.4 异常\n",
    "\n",
    "写代码的时候，出现错误不可避免，即使代码语法没有问题，也可能遇到其它问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看下面这段代码：\n",
    "\n",
    "```python \n",
    "\n",
    "import math\n",
    "\n",
    "while True:\n",
    "    text = input('> ')\n",
    "    if text[0] == 'q':\n",
    "        break\n",
    "    x = float(text)\n",
    "    y = math.log10(x)\n",
    "    print(\"log10({0}) = {1}\".format(x, y))\n",
    "```\n",
    "\n",
    "这段代码接收命令行的输入，当输入为数字时，计算它的对数并输出，直到输入值为 `q` 为止。\n",
    "\n",
    "乍看没什么问题，然而当我们输入 0 或者负数时："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    text = input('> ')\n",
    "    if text[0] == 'q':\n",
    "        break\n",
    "    x = float(text)\n",
    "    y = math.log10(x)\n",
    "    print(\"log10({0}) = {1}\".format(x, y))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`log10` 函数会报错，因为不能接受非正值。\n",
    "\n",
    "一旦报错，程序就会停止执行。如果不希望程序停止执行，并且想要捕捉异常，那么我们可以按照 `try/except` 语句。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "\n",
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = math.log10(x)\n",
    "        print(\"log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一旦 `try` 块中的内容出现了异常，那么 `try` 块后面的内容会被忽略，**Python** 会寻找 `except` 里面有没有对应的内容，如果找到，就执行对应的块，没有则抛出这个异常。\n",
    "\n",
    "在上面的例子中，`try` 抛出的是 `ValueError`，`except` 中有对应的内容，所以这个异常被 `except` 捕捉到，程序可以继续执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = math.log10(x)\n",
    "        print(\"log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 捕捉不同的错误类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``` python\n",
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n",
    "```\n",
    "\n",
    "假设我们将这里的 `y` 更改为 `1 / math.log10(x)`，此时输入 `1`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因为我们的 `except` 里面并没有 `ZeroDivisionError`，所以会抛出这个异常，我们可以通过两种方式解决这个问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 捕捉所有异常\n",
    "\n",
    "将`except` 的值改成 `Exception` 类，来捕获所有的异常。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1 / log10({0}) = {1}\".format(x, y))\n",
    "    except Exception:\n",
    "        print(\"invalid value\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 指定特定异常\n",
    "\n",
    "这里，我们把 `ZeroDivisionError` 加入 `except` 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1 / log10({0}) = {1}\".format(x, y))\n",
    "    except (ValueError, ZeroDivisionError):\n",
    "        print(\"invalid value\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者另加处理："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1 / log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n",
    "    except ZeroDivisionError:\n",
    "        print(\"the value must not be 1\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "事实上，我们还可以将这两种方式结合起来，用 `Exception` 来捕捉其他的错误："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1 / log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n",
    "    except ZeroDivisionError:\n",
    "        print(\"the value must not be 1\")\n",
    "    except Exception:\n",
    "        print(\"unexpected error\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 得到异常的具体信息\n",
    "\n",
    "在上面的例子中，当我们输入不能转换为浮点数的字符串时，它输出的是 `the value must be greater than 0`，这并没有反映出实际情况。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了得到异常的具体信息，我们将这个 `ValueError` 具体化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1 / log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError as exc:\n",
    "        if exc.message == \"math domain error\":\n",
    "            print(\"the value must be greater than 0\")\n",
    "        else:\n",
    "            print(\"could not convert '%s' to float\" % text)\n",
    "    except ZeroDivisionError:\n",
    "        print(\"the value must not be 1\")\n",
    "    except Exception as exc:\n",
    "        print(\"unexpected error:\", exc.message)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同时，我们也将捕获的其他异常的信息显示出来。\n",
    "\n",
    "这里，`exc.message` 显示的内容是异常对应的说明，例如\n",
    "\n",
    "    ValueError: could not convert string to float: a\n",
    "\n",
    "对应的 `message` 是 \n",
    "\n",
    "    could not convert string to float: a\n",
    "\n",
    "当我们使用 `except Exception` 时，会捕获所有的 `Exception` 和它派生出来的子类，但不是所有的异常都是从 `Exception` 类派生出来的，可能会出现一些不能捕获的情况，因此，更加一般的做法是使用这样的形式：\n",
    "\n",
    "```python\n",
    "try:\n",
    "    pass\n",
    "except:\n",
    "    pass\n",
    "```\n",
    "\n",
    "这样不指定异常的类型会捕获所有的异常，但是这样的形式并不推荐。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### else\n",
    "\n",
    "`try/except` 块有一个可选的关键词 `else`。\n",
    "\n",
    "如果使用这个子句，那么必须放在所有的 except 子句之后。else 子句将在 try 子句没有发生任何异常的时候执行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(1)\n",
    "except:\n",
    "    pass\n",
    "else:\n",
    "    print('else was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "出现异常，else 不会执行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(1/0)\n",
    "except ZeroDivisionError:\n",
    "    print('divide by 0.')\n",
    "else:\n",
    "    print('else was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### finally\n",
    "\n",
    "`try/except` 块还有一个可选的关键词 `finally`。\n",
    "\n",
    "不管 `try` 块有没有异常， `finally` 块的内容总是会被执行，而且会在抛出异常前执行，因此可以用来作为安全保证，比如确保打开的文件被关闭。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(1)\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在抛出异常前执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(1 / 0)\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果异常被捕获了，在最后执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(1 / 0)\n",
    "except ZeroDivisionError:\n",
    "    print('divide by 0.')\n",
    "finally:\n",
    "    print('finally was called.')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "异常的处理流程可参考下图：\n",
    "\n",
    "<img src=\"https://www.runoob.com/wp-content/uploads/2019/07/try_except_else_finally.png\" width=600px/>\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.5 警告\n",
    "\n",
    "出现了一些需要让用户知道的问题，但又不想停止程序，这时候我们可以使用警告：\n",
    "\n",
    "首先导入警告模块："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在需要的地方，我们使用 `warnings` 中的 `warn` 函数：\n",
    "\n",
    "    warn(msg, WarningType = UserWarning)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def month_warning(m):\n",
    "    if not 1<= m <= 12:\n",
    "        msg = \"month (%d) is not between 1 and 12\" % m\n",
    "        warnings.warn(msg, RuntimeWarning)\n",
    "\n",
    "month_warning(13)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时候我们想要忽略特定类型的警告，可以使用 `warnings` 的 `filterwarnings` 函数：\n",
    "\n",
    "    filterwarnings(action, category)\n",
    "\n",
    "将 `action` 设置为 `'ignore'` 便可以忽略特定类型的警告："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "warnings.filterwarnings(action = 'ignore', category = RuntimeWarning)\n",
    "\n",
    "month_warning(13)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.6 文件读写\n",
    "\n",
    "写入测试文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile test.txt\n",
    "this is a test file.\n",
    "hello world!\n",
    "python is good!\n",
    "today is a good day.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 读文件\n",
    "\n",
    "使用 `open` 函数来读文件，使用文件名的字符串作为输入参数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('test.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "默认以读的方式打开文件，如果文件不存在会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('test1.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以使用 `read` 方法来读入文件中的所有内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "text = f.read()\n",
    "print(text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以按照行读入内容，`readlines` 方法返回一个列表，每个元素代表文件中每一行的内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('test.txt')\n",
    "lines = f.readlines()\n",
    "print(lines)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用完文件之后，需要将文件关闭。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "事实上，我们可以将 `f` 放在一个循环中，得到它每一行的内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('test.txt')\n",
    "for line in f:\n",
    "    print(line)\n",
    "f.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "删除刚才创建的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('test.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 写文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们使用 `open` 函数的写入模式来写文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('myfile.txt', 'w')\n",
    "f.write('hello world!')\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 `w` 模式时，如果文件不存在会被创建，我们可以查看是否真的写入成功："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(open('myfile.txt').read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果文件已经存在， `w` 模式会覆盖之前写的所有内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('myfile.txt', 'w')\n",
    "f.write('another hello world!')\n",
    "f.close()\n",
    "print(open('myfile.txt').read())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了写入模式，还有追加模式 `a` ，追加模式不会覆盖之前已经写入的内容，而是在之后继续写入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('myfile.txt', 'a')\n",
    "f.write('... and more')\n",
    "f.close()\n",
    "print(open('myfile.txt').read())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "写入结束之后一定要将文件关闭，否则可能出现内容没有完全写入文件中的情况。\n",
    "\n",
    "还可以使用读写模式 `w+`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('myfile.txt', 'w+')\n",
    "f.write('hello world!')\n",
    "f.seek(6)\n",
    "print(f.read())\n",
    "f.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里 `f.seek(6)` 移动到文件的第6个字符处，然后 `f.read()` 读出剩下的内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "删除刚才创建的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('myfile.txt')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 关闭文件\n",
    "\n",
    "在 **Python** 中，如果一个打开的文件不再被其他变量引用时，它会自动关闭这个文件。\n",
    "\n",
    "所以正常情况下，如果一个文件正常被关闭了，忘记调用文件的 `close` 方法不会有什么问题。\n",
    "\n",
    "关闭文件可以保证内容已经被写入文件，而不关闭可能会出现意想不到的结果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('newfile.txt','w')\n",
    "f.write('hello world')\n",
    "g = open('newfile.txt', 'r')\n",
    "print(repr(g.read()))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虽然这里写了内容，但是在关闭之前，这个内容并没有被写入磁盘。\n",
    "\n",
    "使用循环写入的内容也并不完整："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('newfile.txt','w')\n",
    "for i in range(30):\n",
    "    f.write('hello world: ' + str(i) + '\\n')\n",
    "\n",
    "g = open('newfile.txt', 'r')\n",
    "print(g.read())\n",
    "f.close()\n",
    "g.close()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('newfile.txt')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "出现异常时候的读写："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('newfile.txt','w')\n",
    "for i in range(30):\n",
    "    x = 1.0 / (i - 10)\n",
    "    f.write('hello world: ' + str(i) + '\\n')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "查看已有内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = open('newfile.txt', 'r')\n",
    "print(g.read())\n",
    "f.close()\n",
    "g.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，出现异常的时候，磁盘的写入并没有完成，为此我们可以使用 `try/except/finally` 块来关闭文件，这里 `finally` 确保关闭文件，所有的写入已经完成。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('newfile.txt','w')\n",
    "try:\n",
    "    for i in range(30):\n",
    "        x = 1.0 / (i - 10)\n",
    "        f.write('hello world: ' + str(i) + '\\n')\n",
    "except Exception:\n",
    "    print(\"something bad happened\")\n",
    "finally:\n",
    "    f.close()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = open('newfile.txt', 'r')\n",
    "print(g.read())\n",
    "g.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### with 方法\n",
    "\n",
    "事实上，**Python** 提供了更安全的方法，当 `with` 块的内容结束后，**Python** 会自动调用它的`close` 方法，确保读写的安全："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('newfile.txt','w') as f:\n",
    "    for i in range(30):\n",
    "        x = 1.0 / (i - 10)\n",
    "        f.write('hello world: ' + str(i) + '\\n')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与 `try/exception/finally` 效果相同，但更简单。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = open('newfile.txt', 'r')\n",
    "print(g.read())\n",
    "g.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，写文件时候要确保文件被正确关闭。\n",
    "\n",
    "删除刚才创建的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('newfile.txt')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2.7 CSV 文件和 csv 模块\n",
    "\n",
    "标准库中有自带的 `csv` 模块处理 `csv` 格式的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 读 csv 文件\n",
    "\n",
    "假设我们有这样的一个文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%file data.csv\n",
    "\"alpha 1\",  100, -1.443\n",
    "\"beat  3\",   12, -0.0934\n",
    "\"gamma 3a\", 192, -0.6621\n",
    "\"delta 2a\",  15, -4.515\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "打开这个文件，并产生一个文件 reader："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 打开 data.csv 文件\n",
    "fp = open(\"data.csv\")\n",
    "\n",
    "# 读取文件“”\n",
    "r = csv.reader(fp)\n",
    "\n",
    "# 可以按行迭代数据\n",
    "for row in r:\n",
    "    print(row)\n",
    "\n",
    "# 关闭文件\n",
    "fp.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "默认数据内容都被当作字符串处理，不过可以自己进行处理："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = []\n",
    "\n",
    "with open('data.csv') as fp:\n",
    "    r = csv.reader(fp)\n",
    "    for row in r:\n",
    "        data.append([row[0], int(row[1]), float(row[2])])\n",
    "\n",
    "data\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "清除刚刚创建的文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove('data.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 写 csv 文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以使用 `csv.writer` 写入文件，不过相应地，传入的应该是以写方式打开的文件，不过一般要用 `'wb'` 即二进制写入方式，防止出现换行不正确的问题："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = [('one', 1, 1.5), ('two', 2, 8.0)]\n",
    "with open('out.csv', 'w') as fp:\n",
    "    w = csv.writer(fp)\n",
    "    w.writerows(data)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "显示结果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cat 'out.csv'"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "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.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
