程序执行过程中,Python解释器会检测程序是否存在语法错误。即便没有语法错误,还有可能出现逻辑错误。
程序运行期间检测到的错误称为异常。程序出现异常都不会被程序处理,都会以错误信息展现出来。若异常不被处理,程序默认的处理方式是直接崩溃、终止运行。
任务1:输入控制
编写程序s12A.py,控制输入的数据必须是大于100奇数。要求:
1、如果输入数据无效,要求重新输入。(利用try语句控制)
2、如果输入数据有效,则输出该整数的最小质因数。
3、定义函数getp(n),返回奇数n(n>100)的最小质因数。
"""
(1)捕获异常(教材P119)
① try...except...语句:
try:
可能出错的代码
......
except [异常类型]:
错误处理语句
......
例如:
n=input("输入一个正整数:")
try:
n=int(n)
print(10/n)
except:
print("异常")
分析:
输入"0",语句print(10/n)会出现被0除情况;输入"12.3",语句n=int("12.3")会出现参数错误。(int(12.3)=12,但int("12.3")会出错)
这里输出“异常”,只是笼统判断,有异常情况出现了,无法获取异常的详细信息!
② 捕获异常信息:
try:
可能产生异常的代码块
except [ (Error1, Error2, ... ) [as e] ]:
处理异常的代码块1
except [Exception]:
处理其它异常
---(Error1、Error2)都是具体的异常类型。一个except 块可以同时处理多种异常。
---[as e]:可选参数。表示给异常类型起一个别名e,这样做的好处是方便在except块中调用异常类型。
---[Exception]:可选参数。Exception是所有程序异常类的父类,表示程序可能发生的所有异常情况。教材表10-1中,都是Exception的子类。通常用在最后一个except块。
例如:
n=input("输入一个正整数:")
try:
n=int(n)
print(10/n)
except TypeError as e: #①
print(e)
except ValueError as e: #②
print(e)
except Exception as error: #③
print(error)
分析:
输入"12.3",语句n=int("12.3")会出现参数错误。代码②会捕获到ValueError,会输出具体的错误信息: invalid literal for int() with base 10: '12.3'
输入"0",语句print(10/n)会出现被0除情况,这时的异常类型为ZeroDivisionError(教材表10-1)。代码①和②不能捕获到,但会被代码③捕获,输出: division by zero
③ else子句与finally子句
try:
可能出错的语句
......
except:
出错后的执行语句
else:
未出错时的执行语句
这里的else子句,和循环语句中的else子句类似。循环语句搭配的else子句,表示循环正常执行后,要执行的代码。这里与try搭配的else子句,表示try子句未出现异常时,要执行的代码。
finally子句与try-except语句连用时,无论try-except是否捕获到异常,finally子句中的代码都要执行。
(2)assert断言语句(教材P125)
assert断言语句用于判定一个表达式是否为真,如果表达式为True,不做任何操作,否则引发AssertionError异常。
assert 表达式 [,参数]
---表达式是assert语句的判定对象。
---参数通常是一个自定义异常或显示异常描述信息的字符串。
例如:
while True:
n=input("输入一个正整数:")
try:
n=int(n)
assert (n>0),"必须是正整数"
break
except Exception as error:
print(error)
#pass/continue
print(n)
分析:
输入"-100",表达式(n>0)不成立,会引发AssertionError异常,except Exception语句捕获AssertionError异常后,输出信息“必须是正整数”。(前面几个案例中,输入小于0的整数,都不会引发异常)。
注意,except子句中,如果没有print(error)语句,也就是说捕获异常后不作处理,后面必须有pass或continue语句,否则except子句的分支缺失,会有语法错误。有print(error)语句的情况下,pass或continue语句就不必要了。因为处理异常后,自动会进入下一轮循环语句的判断。
(3)定义函数getp(n),返回n的最小质因数。
和实验8中S8D.py判断n是否为质数的函数类似。
def isp(n):
for i in range(2,n):
if n%i==0:
return False
else:
return True
区别在于,函数isp(n)返回值为True或False,不需要关心变量i的具体值。而函数getp(n)中,条件(n%i==0)第1次成立时的变量i的值,就是n的最小质数。例如,n=105时,应该返回3,而不会是后面的5、15等其他因子。
条件(n%i==0)如果一次也不成立,说明n本身就是质数。例如,n=101时,应该返回101,因为101是质数,并且是n的因子。
提示:
“控制输入的数据必须是大于100奇数”,应该在主程序中实现,函数getp(n)只需要返回n的最小质因子就可以了。既然n是奇数,那么2不可能是n的因子,函数getp(n)中的范围,可以是range(3,n)。
任务2:身份证号码有效性检测
身份证号码由6位地址码、8位日期(年4位,月2位,日2位)、3位顺序码和1位校验码组成。
编写程序s12B.py,检测输入的身份证号码的有效性。要求:
1、日期中的年必须是[1900,2022]上的整数
2、日期数据必须有效
3、校验码必须正确。其计算规则如下:
(1)前17位数字加权求和s,权重分别为:7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2。
(2)将s除以11求余,得到值n。
"""n=s%11。n的值只可能是【0,1,2,3,4,5,6,7,8,9,10】这11个数字。
(3)m=12-n,校验码为m除以11的余数。如果余数为10,则校验码用罗马字母"X"代替。
"""校验码为m除以11的余数,即(12-n))% 11。
n的值:【0,1,2,3,4,5,6,7,8,9,10】,对应校验码的值:【1,0,X,9,8,7,6,5,4,3,2】
4、编写函数check(s),利用try语句和assert语句检测身份证号码s的有效性。
(1)如果s无效,函数返回值为0
(2)如果s有效,函数返回值为1
5、程序运行效果:输入身份证号码有效时输出"Pass",否则输出"ERROR"
程序填空:
def check(s): # 身份证号码校验
a = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2) # 计算校验码的权值
try:
assert ( ① ), "长度错误。长度必须是18位"
n = 0
for k in range(17): # 前17个字符必须是数字
assert ( ② ), "格式错误。前17位必须是数字"
n += int(s[k]) * a[k] # 加权求和,s[k]代表身份证各位数字,a[k]代表对应位数的权重
y = ( ③ ) # 得到年份
assert ( ④ ), "年份错误。年份范围只能是【1900~2022】"
m = int(s[10:12]) # 得到月份
assert ( ⑤ ), "月份错误。月份范围只能是【1~12】"
d = int(s[12:14]) # 得到天份
if m == 2:
if ( ⑥ ): # 判断y是否为闰年
assert 1 <= d <= 29, "天份错误。闰年2月天份范围只能是【1~29】"
else:
assert 1 <= d <= 28, "天份错误。非闰年2月天份范围只能是【1~28】"
elif ( ⑦ ): # 小月指4月、6月、9月、11月
assert 1 <= d <= 30, "天份错误。小月天份范围只能是【1~30】"
else:
assert 1 <= d <= 31, "天份错误。大月天份范围只能是【1~31】"
n = (12 - (n % 11)) % 11 # 计算校验码
if n == 10: # 校验码n==10,身份证最末位必须是“X”
if s[17] == "X":
return 1
else:
return 0
else:
if ( ⑧ ): # 校验码n必须和身份证最末位s[17]一致,s[17]为字符,n为整型
return 1
else:
return 0
except Exception as error: # 捕获异常
print( ⑨ ) #输出异常信息
return 0
#主程序
s = input("输入身份证号码:")
if ( ⑩ ): #调用check( )函数
print("Pass")
else:
print("ERROR")

