19.异常处理

目录

一、异常的类型

1.NameError

2.SyntaxError

3.TypeError

4.ValueError

5.IndexError

6.KeyError

二、异常处理

1.try/except语句的基本结构

2.捕获异常的描述信息

3.finally语句与终止行为

三、主动触发异常

1.raise语句

2.assert语句

四、第十七课函数基础练习答案

1.函数基础练习

2.寻找回文数

3.用户注册登陆系统


异常指程序在运行时发生的错误。如果没有提前对可能出现的错误设置处理过程,程序将自动停止。

异常处理主要指的是对异常的捕获、处理及引发异常。

一、异常的类型

异常在python中是一种对象,对应不同原因引起的错误有不同的错误类型。

一旦某段代码程序无法处理时,就会引发异常。如下:输出一个未曾定义过的变量a时,会引发异常。此时,计算机会自动输出异常信息,从而提示程序员代码哪里发生了何种错误。

下面我们来看一些常见的异常类型。

1.NameError

未找到本地或全局变量,即在访问一个未声明的变量。异常信息中包含未定义的变量名称。

print(a)

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 1, in <module>
    print(a)
NameError: name 'a' is not defined

2.SyntaxError

语法错误也很常见。如在if语句后面漏写了冒号。

if 3>5
    print(3)
else:
    print(5)

  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 1
    if 3>5
          ^
SyntaxError: invalid syntax

3.TypeError

类型错误。用户将某些操作应用在不合适的类型对象时,将引发此类错误。例如通过索引方法修改字符串中的某个值就属于类型错误。

name = 'der'
name[0] = 'e'

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 2, in <module>
    name[0] = 'e'
TypeError: 'str' object does not support item assignment

4.ValueError

值错误。当操作或函数接收到类型正确但值不合适的参数时引发。

n = 'k'
print(int(n))

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 2, in <module>
    print(int(n))
ValueError: invalid literal for int() with base 10: 'k'

5.IndexError

索引错误。当使用序列中不存在的索引时引发。

lst = [1,2]
print(lst[2])

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 2, in <module>
    print(lst[2])
IndexError: list index out of range

6.KeyError

键错误。当使用映射中不存在的键时引发。

mydict = {1:'a',2:'b'}
print(mydict[3])

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 2, in <module>
    print(mydict[3])
KeyError: 3

二、异常处理

当你不想在异常发生时结束你的程序,可以使用try/except语句来捕捉并处理异常情况。

try/except语句用以检测try语句块中是否存在错误,从而让except语句捕捉异常并处理它。

1.try/except语句的基本结构

处理任意异常
try:
<语句块>      # 需要监测的代码
except:      # try中发生错误就执行该except中的语句块
<语句块>      #


处理指定异常
try:
<语句块>      # 需要监测的代码
except <异常名称1>:
<语句块>      # 发生异常名称1时的处理代码
except <异常名称2 as result>: # result用于接收异常处理信息
<语句>        # 输出异常信息、处理异常
else:
<语句>        # 如果没有异常发生将要执行的语句

在处理任意异常时,先执行try中的语句块,如果某行代码发生异常,将不再执行后面的语句,而是执行except中的语句块,然后执行try语句后面的代码。

try:
    print(a)
except:
    print(1)

# 输出
1

在处理指定异常时,先执行try中的语句块,如果某行代码发生异常,将执行第一个匹配该异常的except的子句,然后执行try语句后面的代码。

try:
    print(a)
except ValueError:
    print(1)
except NameError:
    print(2)

# 输出
2

一个except中也可以设定多个异常,将他们放在元组中。

try:
    lst = [1,2,3,4]
    print(lst.keys())
except (ValueError,NameError):
    print(1)
except (KeyError,IndexError):
    print(2)
except (TypeError,AttributeError):
    print(3)

# 输出
3 # 错误的原因是列表没有keys方法

2.捕获异常的描述信息

在except中使用as关键字,用变量来接收异常信息。

try:
    lst = [1,2,3,4]
    print(lst.keys())
except (ValueError,NameError) as result:
    print(1)
    print(result)
except (KeyError,IndexError) as result:
    print(2)
    print(result)
except (TypeError,AttributeError) as result:
    print(3)
    print(result)

# 输出
3
'list' object has no attribute 'keys'

执行时先运行try中的语句块,如果发生异常,将执行第一次匹配该异常的except语句,同时将异常信息存储到变量result中。

在python中BaseException是所有异常的基类,如果通过使用BaseException来捕获任意异常,就不用预先知道将要发生何种异常。

try:
    lst = [1,2,3,4]
    print(lst.keys())
except BaseException as result:
    print(1)
    print(result)

# 输出
1
'list' object has no attribute 'keys'

3.finally语句与终止行为

try语句完整结构是try-except-else-finally。在try语句执行过程中如果没有发生任何异常,则程序在执行完try语句块后会进入else语句块执行;finally语句中是无论是否发生异常都将执行finally中的语句。

try:
    int('a')
except Exception as result:
    print(1)
    print(result)
else:
    print(2)
finally:
    print('try is end')

# 输出
1
invalid literal for int() with base 10: 'a'
try is end

注意:else和finally语句都是可选的,且finally语句必须在整个结构的末尾。else语句不能与没有except语句的异常处理结构配合使用,不然会引发语法错误。

在程序中,有一种情况是,无论是否捕捉到异常,都要执行一些终止行为,比如关闭文件、释放锁等,这时可以使用finally语句进行处理。

try:
    file = open('test.txt','r+')
    while True:
        content = file.read()
        if len(content) == 0:
            break
        print(content)
finally:
    file.close()
    print('关闭文件')

# 输出
This is a test file.
关闭文件

在try语句中使用读写方法open打开一个已经存在名为“test”的txt文件,接着在while语句中读取文档中的内容存到content中,如果content中有内容则输出它,最后在finally中使用close方法关闭文件。

三、主动触发异常

1.raise语句

通常来说当程序发生错误时会触发异常,除此以外,有时我们会需要主动触发异常。例如:当值小于0时,触发ValueError。

number = -1
if number < 0:
    raise ValueError

# 输出
Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 3, in <module>
    raise ValueError
ValueError

对异常添加描述信息。

number = -1
if number < 0:
    raise ValueError('值小于0')

# 输出
Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 3, in <module>
    raise ValueError('值小于0')
ValueError: 值小于0

使用不带参数的raise可以再次引发异常。常用需要对异常信息进一步判断,如果满足某种条件,则触发异常,否则做其他处理。

当n值小于0时抛出异常,如果n的值为-2,则打印n的绝对值,否则引发异常。
n = -3
try:
    if n < 0:
        raise ValueError('值小于0,且不等于-2')
    else:
        print(n)
except Exception as result:
    if n == -2:
        print(abs(n))
    else:
        raise

# 输出
Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 4, in <module>
    raise ValueError('值小于0,且不等于-2')
ValueError: 值小于0,且不等于-2

异常引发异常。如果要在异常中抛出另一个异常,可以使用raise-from语句。

try:
    num
except Exception as result:
    raise IndexError('下标超出范围') from result


# 输出
Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 2, in <module>
    num
NameError: name 'num' is not defined

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/binhu/PycharmProjects/learnpython/temp.py", line 4, in <module>
    raise IndexError('下标超出范围') from result
IndexError: 下标超出范围

2.assert语句

assert语句又称为断言,指的是期望用户满足指定的条件。当用户定义的约束条件不满足的时候,它会触发AssertionError异常,所以assert语句可以当作条件式的raise语句。

assert语句的格式为:

assert 逻辑表达式,data   # data为可选,通常为字符串

等效于:

if not 逻辑表达式:
    raise AssertionError(data)

示例:

程序中a=0,第三行断言a的值必须大于等于1,抛出异常,异常信息为"a的值不能小于1"。except语句将异常信息传递给result,第五行输出异常信息。

a= 0
try:
    assert a>=1,"a的值不能小于1"
except Exception as result:
    print(result)

# 输出
a的值不能小于1

注:自定义异常在后续“对象”章节中进行讲解。

四、第十七课函数基础练习答案

1.函数基础练习

# 任务1:lst中有[1,2,3,4,5,6,7,8,9],定义一个名为add_odd()的函数,参数为x用以接收lst,在函数中将所有奇数存到一个新的列表中,返回给调用函数,并计算输出新列表的和。
# 在此处下方定义add_odd()
def add_odd(x):
    odd_lst = []
    for i in x:
        if i % 2 != 0:
            odd_lst.append(i)
    return odd_lst
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_lst = add_odd(lst)
print(odd_lst)
# 在此处次方编写函数调用语句


# 任务2:写一个函数,判断用户传入的对象长度是否大于5
def getlen(x):
    return len(x) > 5

result = getlen(input("请用户输入任意内容:"))
print(result)

# 任务3:编写函数,求“1/(1*2)-1/(2*3)+1/(3*4)-1/(4*5)+……”前n项的和,将n作为参数,n由用户输入。
def func(n):
    sum = 0
    for i in range(1,n+1):
        if i % 2 == 0:
            sum -= 1/(i*(i+1))
        else:
            sum += 1/(i*(i+1))
    return sum

summary = func(int(input("请用户输入任意整数:")))
print(summary)

2.寻找回文数

# 回文数是一个正向和逆向都相同的数,如12321、7887等,请编写函数找出1到n之间的所有回文数,并将其存入列表中打印出来。将n作为参数,n由用户输入。

def func(n):
    lst = []
    for i in range(n+1):
        if str(i) == str(i)[::-1]:
            lst.append(i)
    return lst
print(func(int(input("请输入任意整数"))))

3.用户注册登陆系统

# 子任务1: 创建一个名为displaymenu的函数,展示如下图所示的系统菜单。
# ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
# 用户注册登陆系统
# 1用户注册
# 2用户登陆
# 3退出系统
# ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
def displaymenu():
    print("*" * 30)
    print("User Login System".center(30))
    lst = ["用户注册", "用户登陆", "退出系统"]
    for i in range(len(lst)):
        print(i + 1, lst[i])
    print("*" * 30)

# 子任务2: 创建一个名为getchoice的函数,提示“请选择您想要操作的序号”。要求对用户输入序号的正确性进行判断,如果序号正确,则返回该序号的整形。
def getchoice():
    userinput = input("请选择您想要操作的序号")
    if userinput.isdigit():
        userinput = int(userinput)
        if userinput in range(1, 4):
            return userinput
        else:
            print("序号不在范围内,请重新输入")
            getchoice()
    else:
        print("请输入正确的序号")
        getchoice()


# 子任务3: 创建一个名为user_register的函数,提示用户依次输入用户名、密码、确认密码,并对以上信息按以下规则依次进行检查:
# (1)用户名不重复,且以字母开头、用户名长度在6到30位之间,可包含字母、数字和下划线,以小写保存。
# (2)密码不得少于6位,包含字母、数字和下划线其中至少2种。
# (3)确认密码与输入密码必须一致。
# (4)注册成功时提示用户已成功注册。
# 其他要求:检验发现格式错误的,应给予用户错误提示,直到用户注册成功才退回到系统菜单页面。用户名和密码以
# {username: (username, password)}
# 键值对的形式存储在名为userinfo的字典中。
def user_register():
    username = ""

    def get_username():
        nonlocal username
        username = input("请输入用户名'由字母、数字、下划线组成,长度不少于6个字符':")
        if 6 > len(username) > 30:
            print("长度不足6个字符")
            get_username()
        else:
            yesorno = True
            for i in username:
                if 'a' <= i <= 'z' or i >= 'A' and i <= 'A' or ord(str(i)) >= 48 and ord(
                        str(i)) <= 57 or i == '_':
                    continue
                else:
                    yesorno = False
            if yesorno:
                if username in userinfo.keys():
                    print("用户名已存在")
                    get_username()
            else:
                print("用户名中包含非法字符")
                get_username()

    def get_password():
        password = input("请输入密码'由字母、数字、下划线至少两种组成,长度不少于6个字符':")
        if len(password) < 6:
            print("长度不足6")
            get_password()
        else:
            if password[0].isalpha() or password[0] == '_':  # 判断第一个字符是否字母、下划线
                if not password.isalpha() and not password.isdigit():  # 判断密码是否全部为数字或字母
                    check_password = input("请再次输入密码:")
                    if password == check_password:
                        userinfo[username] = (username, password)
                    else:
                        print("密码不一致")
                        get_password()
                else:
                    print("密码应至少包含数字、字母或下划线中的两种")
                    get_password()
            else:
                print("首字母不得为数字")
                get_password()

    get_username()
    get_password()


# 子任务4: 创建一个名为user_logo的函数,提示用户依次输入用户名、密码,并对以上信息按以下规则依次进行验证:
# (1)检查用户名是否已存在于userinfo中,如果不存在,则提示该用户名不存在。
# (2)继续判断用户名匹配的密码是否一直,如果不匹配则提示密码错误。
# (3)登陆成功时提示用户成功登陆。
def user_logo():
    username = input("请输入用户名")
    password = input("请输入密码")
    if username not in userinfo.keys():
        print("用户名不存在")
    else:
        if password != userinfo[username][1]:
            print("用户名或密码错误")
        else:
            print("恭喜你,登陆成功!")


# 子任务5: 主程序
# (1)调用displaymenu函数
# (2)调用getchoice函数并获得用户的输入
# (3)根据用户的输入分别匹配调用user_register、user_logo函数,如果用户输入3,则退出本程序。
userinfo = {}
while True:
    displaymenu()
    choice = getchoice()
    if choice == 1:
        user_register()
    elif choice == 2:
        user_logo()
    elif choice == 3:
        exit()

猜你喜欢

转载自blog.csdn.net/qq_40407729/article/details/112574428