# 到目前为止,我们已经写过很多代码了,在调试程序的时候,也遇到过很多次"错误"。当程序遇到"错误"的时候,就会停止运行。而"错误"常常防不胜防。如果不对它们进行处理,程序未免太不"健壮"了。为了增强程序的稳健性,必须对它们进行处理。

在这里插入图片描述

                             知识技能导图
# --------------------------------  错误  ------------------------------------#
# python中的"错误"可以分为两种:语法错误(syntax error)、异常(exception)。
# 语法错误通常是因为语句不符合python语法要求,解释器在解析的时候就会报错。比如:
for i in range(10)
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 6
#     for i in range(10)
#                      ^
# SyntaxError: invalid syntax
# 上面那句话因为缺少":",导致解释器无法解释,于是报错。这个报错行为是由python的语法分析器完成的,并且检测到了错误所在文件和行号(File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 6),还以"^"标示错误位置(后面缺少":"),最后显示错误类型。
# 在程序中还会出现逻辑错误。逻辑错误可能是由于不完整或者不合法对策输入导致的,也可能是无法生成、计算等,或者其他逻辑问题。逻辑错误不是由python来检查的,所以此处所谈的错误不包括逻辑错误。
# 对于初学者而言,细节的错误常常令人烦恼,如代码块的缩进、丢掉冒号、单词拼写错误等。所以,在遇到错误的时候要先对细节部分进行检查,排除"低级错误"。

在这里插入图片描述

                             常见的异常和错误类型
# --------------------------------  异常  ------------------------------------#
# --------------------------------  NameError  ------------------------------------#
bar
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 27, in <module>
#     bar
# NameError: name 'bar' is not defined
# 在python中虽然不需要在使用变量之前先声明类型,但需要对变量进行赋值,然后才能使用。不被赋值的变量不能在python中存在,因为变量相当于一个标签,要把它贴到对象上才有意义。

# --------------------------------  异常  ------------------------------------#
# --------------------------------  IndexError和KeyError  ------------------------------------#
a = [1,2,3]
a[4]
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 38, in <module>
#     a[4]
# IndexError: list index out of range

d = {"python":"itdiffer.com"}
d["java"]
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 45, in <module>
#     d["java"]
# KeyError: 'java'
# 这两个都属于"鸡蛋里面挑骨头"的类型,一定得抛出异常。不过在编程实践中,特别是循环的时候,常常由于循环条件设置不合理而出现这种异常。


# --------------------------------  异常  ------------------------------------#
# --------------------------------  IOError  ------------------------------------#
f = open("foo")
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 55, in <module>
#     f = open("foo")
# IOError: [Errno 2] No such file or directory: 'foo'
# open()的作用是打开文件。如果找不到相应的文件,就会出现上述异常。
# 总结:这些都是python的内置异常,当然不局限于这几个,出现了异常不要慌张,这是好事情,是python在帮助修改和优化程序。只要认真阅读异常信息,再用dir(),help()或官方网站文档、Google等来协助,就一定能解决问题。


# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  一般情况  ------------------------------------#
# 如果在程序运行过程中抛出异常,程序就会中止运行。这样的程序是不稳健的,稳健性强的程序应该是不为各种异常所击倒,所以要在程序中对异常进行处理。
# try...except...是常用的异常处理语句。
while True:
    try:
        n = int(input("Please enter integer:"))            #1
        print(n)
        break
    except:
        print("Oo! Try again.")
# Please enter integer:a
# Oo! Try again.
# Please enter integer:2
# 2

# 这段代码先执行try分支下的代码块。如果用户输入的不是整数(如上述操作中输入了"a"),则在#1处发生了异常,就不再执行后面的语句,转而执行except分支下的语句(如上述操作,执行了print(),显示"Oo! Try again.")。因为上述代码是无限循环,所以再次执行了try分支,用户输入了2,没有发生异常,继续执行后面的语句,遇到break中止循环。
# 对于try...except...语句,try分支下是要执行的语句,except则是异常处理语句。上面示例没有规定except所处理的异常类型,在except后面可以申明异常类型。

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  申明异常类型  ------------------------------------#
class Calculator:
    is_raise = False
    def calc(self,express):
        try:
            return eval(express)                     #2
        except ZeroDivisionError:                    #3
            if self.is_raise:
                return "zero can not be division."   #4
            else:
                raise                                #5
if __name__=="__main__":
    c = Calculator()
    print(c.calc("8/0"))
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 99, in <module>
#     print(c.calc("8/0"))
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 91, in calc
#     return eval(express)                     #2
#   File "<string>", line 1, in <module>
# ZeroDivisionError: division by zero

#2 该处应用了一个函数eval,它能实现对字符串形式的表达式进行计算。比如:
# eval("3+5")6
# 8
# 这样,在调用方法calc的时候,给参数express提供的可以是一个字符串形式的表达式,由#2完成最终运算。
#3 此处的express分支明确了要处理的异常类型,其他异常类型这里就不处理了。在编程实践中,不提倡express后面不声明异常类型的做法,不要在一个处理异常的分支中包含太多异常处理,因为这样会让开发者一头雾水。
#4 当self.is_raise为真的时候,返回此处的异常信息。但是,这个类中已经规定了self.is_raise=False,所以默认状态下不会执行这个条件分支,而是执行else分支,即#5。
#5 以raise作为单独的一个语句,其含义是将异常信息抛出。再对照程序执行结果,所显示的错误信息就是raise执行结果。

# 如果将is_raise的值改为True,如下所示:
if __name__=="__main__":
    c = Calculator()
    c.is_raise = True
    print(c.calc("8/0"))
# zero can not be division.
# 没有执行raise语句,是按照条件判断,执行了#4,这样可以控制显示异常处理中的提示信息内容。
# try...except...是处理异常的基本方式。在此基础上还可以扩展,能够处理多个异常。

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  多个异常  ------------------------------------#
# 处理多个异常并不是因为同时报出多个异常。程序在运行中,只要遇到一个异常就会有反应,所以每次捕获到的异常一定是一个。所谓处理多个异常,是可以允许捕获不同的异常,由不同的except子句处理。
while True:
    try:
        a = float(input("first number:"))
        b = float(input("second number:"))
        r = a / b
        print("{}/{}={}".format(a,b,r))
        break
    except ZeroDivisionError:
        print("The second number can not be zero.Try again.")
    except ValueError:
        print("Please enter number.Try again.")
    except:
        break
# first number:6
# second number:0
# The second number can not be zero.Try again.
# first number:6
# second number:a
# Please enter number.Try again.
# first number:6
# second number:4
# 6.0/4.0=1.5

# except后面不仅可以放一个异常类型的名称,还可以放多个。比如上面的示例程序,可以修改为:
while True:
    try:
        a = float(input("first number:"))
        b = float(input("second number:"))
        r = a / b
        print("{}/{}={}".format(a,b,r))
        break
    except (ZeroDivisionError,ValueError):
        print("Try again.")                             #6
    # except ZeroDivisionError:
    #     print("The second number can not be zero.Try again.")
    # except ValueError:
    #     print("Please enter number.Try again.")
    except:
        break
# first number:6
# second number:0
# Try again.
# first number:6
# second number:a
# Try again.
# first number:6
# second number:4
# 6.0/4.0=1.5

# 请把此处的执行过程和结果与前述进行对比,理解except后面多个参数的作用。注意except后面的多个参数,一定要用圆括号包括起来。
#6 当except处理异常的时候,此处即为显示信息。如果觉得这种显示信息不友好,还可以继续修改except子句,将异常中原有的提示信息打印出来。
while True:
    try:
        a = float(input("first number:"))
        b = float(input("second number:"))
        r = a / b
        print("{}/{}={}".format(a,b,r))
        break
    except (ZeroDivisionError,ValueError) as e:    #7
        print(e)
        print("Try again.")
    except:
        break
# first number:6
# second number:0
# float division by zero
# Try again.
# first number:6
# second number:a
# could not convert string to float: 'a'
# Try again.
# first number:6
# second number:4
# 6.0/4.0=1.5

#7 此处的e就引用了每次异常时的默认提示信息。

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  else分支  ------------------------------------#
# try...except...还可以有一个可选的else分支,通常放在所有except的后面,当没有异常的时候,执行该分支下的语句块。
try:
    print("I am in try.")
except:
    print("I am in except.")
else:
    print("I am in else.")
# I am in try.
# I am in else.

# 此处,except没有捕获异常,执行的是try和else两个分支,再对比如下示例:
try:
    print(1/0)
except:
    print("I am in except.")
else:
    print("I am in else.")
# I am in except.

# 这段代码中有异常需要except处理,这时else分支就不被执行了。

# 下面使用else语句对#7所在的程序进行优化。
while True:
    try:
        a = float(input("first number:"))
        b = float(input("second number:"))
        r = a / b
        print("{}/{}={}".format(a,b,r))
        # break                                      #8
    except (ZeroDivisionError,ValueError) as e:
        print(e)
        print("Try again.")
    else:
        break
# first number:6
# second number:0
# float division by zero
# Try again.
# first number:6
# second number:a
# could not convert string to float: 'a'
# Try again.
# first number:6
# second number:4
# 6.0/4.0=1.5

#8 此处的break注释了,但是因为增加了else语句,程序没有陷入"死循环"。

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  finally分支  ------------------------------------#
# 如果说else是try的跟随者,那么另一个名为finally的分支就是"终结者"了。它的作用是不管前面执行哪个分支,最后都要执行它。
try:
    print("I am in try.")
except:
    print("I am in except.")
else:
    print("I am in else.")
finally:
    print("I am in finally.")
# I am in try.
# I am in else.
# I am in finally.

try:
    print(1/0)
except:
    print("I am in except.")
else:
    print("I am in else.")
finally:
    print("I am in finally.")
# I am in except.
# I am in finally.

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  raise语句  ------------------------------------#
# 除了使用try...except...处理异常,有时需要主动抛出异常。raise语句就是完成这个工作的。
def enterage(age):
    if age < 0:
        raise ValueError("Only positive integers are allowed")            #9
    if age % 2 == 0:
        print("age is even")
    else:
        print("age is odd")
try:
    num = int(input("Enter your age:"))
    enterage(num)
except ValueError:
    print("Only integers are allowed")
except:
    print("something is wrong")
# Enter your age:python                                                   #10
# Only integers are allowed
# Enter your age:11
# age is odd
# Enter your age:12
# age is even
# Enter your age:-22                                                      #11
# Only integers are allowed

# 调试程序的时候,如果输入了字符串(如#10),这个异常会被except ValueError捕获;如果输入了负数(如#11),则#9会主动抛出异常。

# --------------------------------  异常处理  ------------------------------------#
# --------------------------------  assert语句  ------------------------------------#
def year(x):
    assert x > 0                                  #12
    return str(x) + "year"
print(year(2018))
# 2018year
print(year(-2018))
# Traceback (most recent call last):
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 316, in <module>
#     print(year(-2018))
#   File "/Users/apple/PycharmProjects/pythonProject3/异常处理.py", line 312, in year
#     assert x > 0
# AssertionError

# 在函数year的#12使用了关键字assert发起的语句。中文将assert译为"断言",它发起的语句等价于条件判断,发生异常就意味着表达式为假。

# --------------------------------  自定义异常类型  ------------------------------------#
# 各种异常其实也是对象,请以自定义对象类型来修改函数enterage。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐