# 14.异常和语法错误

## 概念

## **什么是语法错误？**

语法错误，一般是代码出现错误，导致程序不能运行

```
>>> if True
  File "<stdin>", line 1
    if True
          ^
SyntaxError: invalid syntax
>>> if True:
...     print(True)
...
True
```

这里if语句后面没有写上冒号(:)导致程序不能运行，从而产生了语法错误

## **什么是异常？**

有时候一条语句或表达式在语法上是正确的，但是试图执行语句时可能会引发错误，所以运行期间检测到的错误称为异常

```
>>> 2/3
0.6666666666666666
>>> 2/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
```

第一个表达式2/3能够正常运行，而下面的同样的格式的表达式却曝出ZeroDivisionError错误，这个就是异常，意思是除数不能为0

## 如何捕获异常

python中使用try...except处理程序抛出的异常

### **语法**

```
try:
    语句1
except:
    语句2
except:
    ....


try:
    语句1
except:
    语句2
except:
    ....
else:
    pass
```

try语句按如下方式工作：

* 首先，执&#x884C;_&#x74;r&#x79;_&#x5B50;句 （在try/except和关键字之间的部分）。
* 如果没有异常发生，_excep&#x74;_&#x5B50;句 在try语句执行完毕后就被忽略了。
*   如果在 try 子句执行过程中发生了异常，那么该子句其余的部分就会被忽略。

    如果异常匹配于except关键字后面指定的异常类型，就执行对应的except子句。然后继续执行语句之后的代码。
* 如果最终仍找不到对应的处理语句，它就成为一&#x4E2A;_&#x672A;处理异常_，终止程序运行，显示提示信息。
* 一个 try 语句可能包含多个 except 子句，分别指定处理不同的异常

```
>>> name = int(input())
aad
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'aad'
>>> try:
...     a = int(input())
...     print(a/0)
... except ValueError:
...     print(ValueError)
... except:
...     pass
...
asd
<class 'ValueError'>
```

try … except 语句可以带有一个 else子句，该子句只能出现在所有 except 子句之后。当 try 语句没有抛出异常时，需要执行一些代码，可以使用这个子句

```
>>> try:
...     print("正常")
... except:
...     print("错误")
... else:
...     print("还要运行")
...
正常
还要运行
```

使用else子句比在try子句中附加代码要好，因为这样可以避免try…except意外的截获本来不属于它们保护的那些代码抛出的异常。

发生异常时，可能会有一个附属值，作为异常&#x7684;_&#x53C2;&#x6570;_&#x5B58;在。这个参数是否存在、是什么类型，依赖于异常的类型。

在异常名（列表）之后，也可以为 except 子句指定一个变量。这个变量绑定于一个异常实例，它存储在`instance.args`的参数中。为了方便起见，异常实例定义了\_\_str\_\_()，这样就可以直接访问过打印参数而不必引用`.args`。这种做法不受鼓励。相反，更好的做法是给异常传递一个参数（如果要传递多个参数，可以传递一个元组），把它绑定到 message 属性。一旦异常发生，它会在抛出前绑定所有指定的属性。

```
>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print(type(inst))    # the exception instance
...    print(inst.args)     # arguments stored in .args
...    print(inst)          # __str__ allows args to be printed directly,
...                         # but may be overridden in exception subclasses
...    x, y = inst.args     # unpack args
...    print('x =', x)
...    print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
```

对于那些未处理的异常，如果一个它们带有参数，那么就会被作为异常信息的最后部分（“详情”）打印出来。

异常处理器不仅仅处理那些在 try 子句中立刻发生的异常，也会处理那些 try 子句中调用的函数内部发生的异常。例如:

```
>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: int division or modulo by zero
```

## 抛出异常

raise 语句允许强制抛出一个指定的异常

实例:

```
>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere
```

要抛出的异常由 raise 的唯一参数标识。它必需是一个异常实例或异常类（继承自 Exception 的类）。

如果你需要明确一个异常是否抛出，但不想处理它，raise 语句可以让你很简单的重新抛出该异常:

```
>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
```

## 怎么自定义异常

在程序中可以通过创建新的异常类型来命名自己的异常（Python 类的内容请参见类`Exception`）。异常类通常应该直接或间接的从类派生，例如:

```
>>> class MyError(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
...
>>> try:
...     raise MyError(2*2)
... except MyError as e:
...     print('My exception occurred, value:', e.value)
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
```

在这个例子中，`Exception.__init__()`默认的被覆盖。新的方式简单的创&#x5EFA;_&#x76;alu&#x65;_&#x5C5E;性。这就替换了原来创建\*`args`\*属性的方式。

异常类中可以定义任何其它类中可以定义的东西，但是通常为了保持简单，只在其中加入几个属性信息，以供异常处理句柄提取。如果一个新创建的模块中需要抛出几种不同的错误时，一个通常的作法是为该模块定义一个异常基类，然后针对不同的错误类型派生出对应的异常子类:

```
class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message
```

与标准异常相似，大多数异常的命名都以 “Error” 结尾。

很多标准模块中都定义了自己的异常，用以报告在他们所定义的函数中可能发生的错误。关于类的进一步信息请参见类一章。
