{
"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
}