{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 异常" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## try & except 块" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "写代码的时候,出现错误必不可免,即使代码没有问题,也可能遇到别的问题。\n", "\n", "看下面这段代码:\n", "\n", "```python \n", "import math\n", "\n", "while True:\n", " text = raw_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": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> -1\n" ] }, { "ename": "ValueError", "evalue": "math domain error", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;32mbreak\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfloat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 8\u001b[1;33m \u001b[0my\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlog10\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 9\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[1;34m\"log10({0}) = {1}\"\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mValueError\u001b[0m: math domain error" ] } ], "source": [ "import math\n", "\n", "while True:\n", " text = raw_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)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`log10` 函数会报错,因为不能接受非正值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一旦报错,程序就会停止执行,如果不希望程序停止执行,那么我们可以添加一对 `try & except`: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> -1\n", "the value must be greater than 0\n", "> 0\n", "the value must be greater than 0\n", "> 1\n", "log10(1.0) = 0.0\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 捕捉不同的错误类型" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "``` python\n", "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n" ] }, { "ename": "ZeroDivisionError", "evalue": "float division by zero", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;32mbreak\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfloat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0my\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;33m/\u001b[0m \u001b[0mmath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlog10\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 10\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[1;34m\"log10({0}) = {1}\"\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mZeroDivisionError\u001b[0m: float division by zero" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "因为我们的 `except` 里面并没有 `ZeroDivisionError`,所以会抛出这个异常,我们可以通过两种方式解决这个问题:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 捕捉所有异常" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "将`except` 的值改成 `Exception` 类,来捕获所有的异常。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "invalid value\n", "> 0\n", "invalid value\n", "> -1\n", "invalid value\n", "> 2\n", "1 / log10(2.0) = 3.32192809489\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 指定特定值" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这里,我们把 `ZeroDivisionError` 加入 `except` 。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "invalid value\n", "> -1\n", "invalid value\n", "> 0\n", "invalid value\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "或者另加处理:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "the value must not be 1\n", "> -1\n", "the value must be greater than 0\n", "> 0\n", "the value must be greater than 0\n", "> 2\n", "1 / log10(2.0) = 3.32192809489\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "事实上,我们还可以将这两种方式结合起来,用 `Exception` 来捕捉其他的错误:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "the value must not be 1\n", "> -1\n", "the value must be greater than 0\n", "> 0\n", "the value must be greater than 0\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 得到异常的具体信息" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在上面的例子中,当我们输入不能转换为浮点数的字符串时,它输出的是 `the value must be greater than 0`,这并没有反映出实际情况。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "ename": "ValueError", "evalue": "could not convert string to float: a", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mfloat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mValueError\u001b[0m: could not convert string to float: a" ] } ], "source": [ "float('a')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "为了得到异常的具体信息,我们将这个 `ValueError` 具现化:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "the value must not be 1\n", "> -1\n", "the value must be greater than 0\n", "> aa\n", "could not convert 'aa' to float\n", "> q\n" ] } ], "source": [ "import math\n", "\n", "while True:\n", " try:\n", " text = raw_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" ] }, { "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": [ "## 自定义异常" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "异常是标准库中的类,这意味着我们可以自定义异常类:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class CommandError(ValueError):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这里我们定义了一个继承自 `ValueError` 的异常类,异常类一般接收一个字符串作为输入,并把这个字符串当作异常信息,例如:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> bad command\n" ] }, { "ename": "CommandError", "evalue": "Invalid commmand: bad command", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mCommandError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mcommand\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mraw_input\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'> '\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mcommand\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvalid_commands\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mCommandError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Invalid commmand: %s'\u001b[0m \u001b[1;33m%\u001b[0m \u001b[0mcommand\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mCommandError\u001b[0m: Invalid commmand: bad command" ] } ], "source": [ "valid_commands = {'start', 'stop', 'pause'}\n", "\n", "while True:\n", " command = raw_input('> ')\n", " if command.lower() not in valid_commands:\n", " raise CommandError('Invalid commmand: %s' % command)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们使用 `raise` 关键词来抛出异常。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以使用 `try/except` 块来捕捉这个异常:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "``` python\n", "valid_commands = {'start', 'stop', 'pause'}\n", "\n", "while True:\n", " command = raw_input('> ')\n", " try:\n", " if command.lower() not in valid_commands:\n", " raise CommandError('Invalid commmand: %s' % command)\n", " except CommandError:\n", " print 'Bad command string: \"%s\"' % command\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "由于 `CommandError` 继承自 `ValueError`,我们也可以使用 `except ValueError` 来捕获这个异常。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## finally" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "try/catch 块还有一个可选的关键词 finally。\n", "\n", "不管 try 块有没有异常, finally 块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭。。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "finally was called.\n" ] } ], "source": [ "try:\n", " print 1\n", "finally:\n", " print 'finally was called.'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在抛出异常前执行:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "finally was called.\n" ] }, { "ename": "ZeroDivisionError", "evalue": "integer division or modulo by zero", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;33m/\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[1;34m'finally was called.'\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mZeroDivisionError\u001b[0m: integer division or modulo by zero" ] } ], "source": [ "try:\n", " print 1 / 0\n", "finally:\n", " print 'finally was called.'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果异常被捕获了,在最后执行:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "divide by 0.\n", "finally was called.\n" ] } ], "source": [ "try:\n", " print 1 / 0\n", "except ZeroDivisionError:\n", " print 'divide by 0.'\n", "finally:\n", " print 'finally was called.'" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }