{ "cells": [ { "cell_type": "markdown", "id": "919b31e1", "metadata": {}, "source": [ "# 异常与警告\n", "\n", "## 异常" ] }, { "cell_type": "markdown", "id": "cb73f95d", "metadata": {}, "source": [ "写代码的时候,出现错误必不可免。\n", "\n", "看下面这段代码:\n", "\n", "```python\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(f\"log10({x}) = {y}\")\n", "```\n", "\n", "这段代码接收命令行的输入,输入为数字时,计算它的对数并输出,直到输入值为 q 为止。\n", "\n", "乍看没什么问题,然而输入0或者负数时:" ] }, { "cell_type": "code", "execution_count": 1, "id": "ce29786e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> -1\n" ] }, { "ename": "ValueError", "evalue": "math domain error", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m<cell line: 4>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m 7\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mfloat\u001b[39m(text)\n\u001b[0;32m----> 8\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[43mmath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlog10\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlog10(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mValueError\u001b[0m: math domain error" ] } ], "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(f\"log10({x}) = {y}\")" ] }, { "cell_type": "markdown", "id": "5cac91cd", "metadata": {}, "source": [ "log10 函数会报错,因为不能接受非正值。\n", "\n", "一旦报错,程序就会停止执行,如果不希望程序停止执行,那么可以添加一对 try & except:" ] }, { "cell_type": "code", "execution_count": 2, "id": "c8fe326c", "metadata": {}, "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 = input('> ')\n", " if text[0] == 'q':\n", " break\n", " x = float(text)\n", " y = math.log10(x)\n", " print(f\"log10({x}) = {y}\")\n", " except ValueError:\n", " print(\"the value must be greater than 0\")" ] }, { "cell_type": "markdown", "id": "83b73dea", "metadata": {}, "source": [ "## 捕捉不同的异常类型\n", "假设将这里的 y 更改为 1 / math.log10(x),此时输入 1:" ] }, { "cell_type": "code", "execution_count": 3, "id": "d7c2c68d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n" ] }, { "ename": "ZeroDivisionError", "evalue": "float division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36m<cell line: 4>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m 8\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mfloat\u001b[39m(text)\n\u001b[0;32m----> 9\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mmath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlog10\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1 / log10(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m:\n", "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero" ] } ], "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(f\"1 / log10({x}) = {y}\")\n", " except ValueError:\n", " print(\"the value must be greater than 0\")" ] }, { "cell_type": "markdown", "id": "5a6b9562", "metadata": {}, "source": [ "程序仍然抛出了异常,原因是`ZeroDivisionError`不在可处理的异常中。\n", "\n", "可以有两种方法处理这个问题,第一种是捕获异常的父类`Exception`:" ] }, { "cell_type": "code", "execution_count": 4, "id": "65df118a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "invalid value\n", "> 0\n", "invalid value\n", "> -1\n", "invalid value\n", "> q\n" ] } ], "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(f\"1 / log10({x}) = {y}\")\n", " except Exception:\n", " print(\"invalid value\")" ] }, { "cell_type": "markdown", "id": "dc05d94b", "metadata": {}, "source": [ "第二种是指定多个错误类型:" ] }, { "cell_type": "code", "execution_count": 5, "id": "c6c68cea", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> 1\n", "invalid value\n", "> 0\n", "invalid value\n", "> -1\n", "invalid value\n", "> q\n" ] } ], "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(f\"1 / log10({x}) = {y}\")\n", " except (ZeroDivisionError, ValueError):\n", " print(\"invalid value\")" ] }, { "cell_type": "markdown", "id": "3de2f776", "metadata": {}, "source": [ "还可以分开处理:" ] }, { "cell_type": "code", "execution_count": 6, "id": "57fd9ccf", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> -1\n", "the value must be greater than 0\n", "> 1\n", "the value must not be 1\n", "> 2\n", "1 / log10(2.0) = 3.321928094887362\n", "> q\n" ] } ], "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(f\"1 / log10({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", "id": "38774e9b", "metadata": {}, "source": [ "还可以将异常的具体信息打出来:" ] }, { "cell_type": "code", "execution_count": 7, "id": "716d8331", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "> -1\n", "math domain error\n", "> 0\n", "math domain error\n", "> 1\n", "float division by zero\n", "> abcde\n", "could not convert string to float: 'abcde'\n", "> q\n" ] } ], "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(f\"1 / log10({x}) = {y}\")\n", " except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "73dd78c8", "metadata": {}, "source": [ "可以用raise主动抛出异常,例如判断月份是否在1-12之间:" ] }, { "cell_type": "code", "execution_count": 8, "id": "01c60d1c", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "13 must between 1 and 12!", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "Input \u001b[0;32mIn [8]\u001b[0m, in \u001b[0;36m<cell line: 3>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m month \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m13\u001b[39m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m month \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m12\u001b[39m:\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmonth\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must between 1 and 12!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mValueError\u001b[0m: 13 must between 1 and 12!" ] } ], "source": [ "month = 13\n", "\n", "if not 1 <= month <= 12:\n", " raise ValueError(f\"{month} must between 1 and 12!\")" ] }, { "cell_type": "markdown", "id": "e16d9e79", "metadata": {}, "source": [ "## finally\n", "\n", "try/catch 块还有一个可选的关键词 finally。\n", "\n", "不管 try 块有没有异常, finally 块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭:" ] }, { "cell_type": "code", "execution_count": 10, "id": "072d0ac9", "metadata": {}, "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", "id": "91acd337", "metadata": {}, "source": [ "如果有异常被抛出,finally的部分会在抛出异常前执行:" ] }, { "cell_type": "code", "execution_count": 11, "id": "5f8e070c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "finally was called.\n" ] }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "Input \u001b[0;32mIn [11]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m)\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mfinally was called.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "try:\n", " print(1 / 0)\n", "finally:\n", " print('finally was called.')" ] }, { "cell_type": "markdown", "id": "ca5dfe89", "metadata": {}, "source": [ "异常被处理了,则在最后执行:" ] }, { "cell_type": "code", "execution_count": 12, "id": "1cdb21e5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "division by zero\n", "finally was called.\n" ] } ], "source": [ "try:\n", " print(1 / 0)\n", "except Exception as e:\n", " print(e)\n", "finally:\n", " print('finally was called.')" ] }, { "cell_type": "code", "execution_count": null, "id": "5fe4fb61", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.10" } }, "nbformat": 4, "nbformat_minor": 5 }