python应用开发实战第二章 异常处理

2.1 异常与错误

  • 错误

    从软件方面来说,错误是语法或是逻辑上的。错误是语法或是逻辑上的。
    语法错误指示软件的结构上有错误,导致不能被解释器解释或编译器无法编译。这些些错误必须在程序执行前纠正。当程序的语法正确后,剩下的就是逻辑错误了。逻辑错误可能是由于不完整或是不合法的输入所致;在其它情况下,还可能是逻辑无法生成、计算、或是输出结果需要的过程无法执行。这些错误通常分别被称为域错误和范围错误。当python检测到一个错误时,python解释器就会指出当前流已经无法继续执行下去。这时候就出现了异常。

  • 异常

    对异常的最好描述是:它是因为程序出现了错误而在正常控制流。以外采取的行为。 这个行为又分为两个阶段:首先是引起异常发生的错误,然后是检测(和采取可能的措施)阶段。第一阶段是在发生了一个异常条件(有时候也叫做例外的条件)后发生的。只要检测到错误并且意识到异常条件,解释器就会发生一个异常。引发也可以叫做触发,抛出或者生成。解释器通过它通知当前控制流有错误发生。python也允许程序员自己引发异常。无论是python解释器还是程序员引发的,异常就是错误发生的信号。当前流将被打断,用来处理这个错误并采取相应的操作。这就是第二阶段。对于异常的处理发生在第二阶段,异常引发后,可以调用很多不同的操作。可以是忽略错误(记录错误但不采取任何措施,采取补救措施后终止程序。)或是减轻问题的影响后设法继续执行程序。所有的这些操作都代表一种继续,或是控制的分支。关键是程序员在错误发生时可以指示程序如何执行。python用异常对象(exception object)来表示异常。遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(traceback)终止执行
    常见的异常列表:python 官方文档
    中文描述

2.2 异常处理

  • try …except 语句
Created with Raphaël 2.2.0 开始 执行try语句 发生异常? 执行except 处理异常 结束 yes no

except后面如果没有指定特定的异常类型,它将会捕捉到try语句中的所有类型的异常,如果后面指定特定的异常类型,可以利用该语句来专门处理该特定异常。

try:
    #正常的操作
   ......................
except ExceptionType, Argument:
    #你可以在这输出 Argument 的值...
#每一次都只会有一个异常处理器被调用
def solve():
	a=int(input('enter a number:'))
	assert a>0 #断言语句,如果a<=0,出现assertionerror
	print ('number enter is Ok')
	d=x+a
	e=2*d
def func():
	try:
		solve()
	except NameError as e: #如果a>0,执行这一句,因为x没有被定义
		print('number error',e.args)
	except AssertionError:
		print('assertionerror')#如果a<=0,执行这一句
if __name__=='__main__':
	func()

上述的e是异常的实例化。通过args参数调用错误信息

  • raise关键字
    raise用于强制让异常发生。在下面的程序中,检测到第三个excpet的异常后,执行完异常处理程序后,利用raise语句再次引发异常。
def solve():
	b=0
	a=int(input('enter a number:'))
	assert a>0
	print ('number enter is Ok')
	a+=a/b
	d=x+a
	e=2*d
def func():
	try:
		solve()
	except NameError as e:
		print('number error',e.args)
	except AssertionError:
		print('assertionerror')
	except Exception as e :
		print('unhandle error ,logging thr error ')
		print(e.args)
		raise
if __name__=='__main__':
	func()

  • try .,.except的else代码块
    else语句紧跟在try…except后,如果try代码块下的语句没有异常才会执行。else代码块会在finally语句之前执行。

  • finally…
    无论是否有异常被抛出,都会执行。相当于是一种全天候保障。

2.3 兽人之袭V1.1.0 异常处理

在上一篇代码的基础上,我们使用了抽象基类AbstractGameUnit代替gameunit。并且针对用户输入增加了异常处理的代码。主要分布在gameunit的heal函数中以及process_users_choice函数中。

# python应用开发实战
# 兽人之袭v1.1.0.面向对象编程
'''
需求分析:
针对用户的输出加入异常处理
'''
import random
import textwrap
import sys
from abc import ABCMeta, abstractmethod

if sys.version_info < (3, 0):
	print('本代码需要在python3.5以上的版本运行')
	print('当前版本号为%d.%d' % (sys.version_info[0], sys.version_info[1]))
	print('正在退出')
	sys.exit(1)


def print_dotted_line(width=72):
	print('-' * width)


def show_theme_message(width=72):
	print_dotted_line()
	print('\033[1;35;34m' + '兽人之袭V0.0.1:' + '\033[0m')
	# ---------------------------------------
	# 输出字体颜色格式的更改
	# 1:高亮显示,35:前景色为紫红色,34:背景色为蓝色
	# 格式为‘\033[显示方式;前景色;背景色 +'text' +'\033[0m'
	msg = (
		'人类和他们的敌人——兽人之间的战争即将开始,'
		'foo爵士,守卫在南部平原的勇敢骑士之一,开'
		'始了一段漫长的旅途。在一个未知的茂密的森'
		'林,他发现了一个小的孤立居住点,因为'
		'疲劳的原因,再加上希望能补充到粮食储备,他'
		'决定绕道而行。当他走进村庄时,他看见5个木屋'
		',周围没有任何敌人。犹豫之后,他决定走进其中'
		'一间木屋......'
	)
	print(textwrap.fill(msg, width=width // 2))  # 调整输出格式,以填充的形式输出


def weighted_random_selection(obj1, obj2):
	weighted_list = 4* [id(obj1)] + 6 * [id(obj2)]
	selction = random.choice(weighted_list)
	if selction == id(obj1):
		return obj1
	else:
		return obj2


def print_bold(msg, end='\n'):
	print('\033[1m' + msg + '\033[0m', end=end)


class AbstractGameUnit(metaclass=ABCMeta):

	def __init__(self, name=''):
		self.max_hp = 0
		self.health_meter = 0
		self.name = name
		self.enemy = None
		self.unit_type = None

	@abstractmethod
	def info(self):
		"""Information on the unit (MUST be overridden in subclasses)"""
		pass

	def attack(self, enemy):
		injured_unit = weighted_random_selection(self, enemy)
		injury = random.randint(10, 15)
		injured_unit.health_meter = max(injured_unit.health_meter - injury, 0)
		print("攻击! ", end='')
		self.show_health(end='  ')
		enemy.show_health(end='  ')

	def heal(self, heal_by=2, full_healing=True):
		if self.health_meter == self.max_hp:
			return
		if full_healing:
			self.health_meter = self.max_hp
		else:
			self.health_meter += heal_by

		print_bold("你已经被治疗!", end=' ')
		self.show_health(bold=True)

	def reset_health_meter(self):

		self.health_meter = self.max_hp

	def show_health(self, bold=False, end='\n'):
		msg = "Health: %s: %d" % (self.name, self.health_meter)

		if bold:
			print_bold(msg, end=end)
		else:
			print(msg, end=end)


class knight(AbstractGameUnit):
	def __init__(self, name='Foo先生'):
		super().__init__(name=name)  # 调用超类的初始化函数
		self.max_hp = 40
		self.health_meter = self.max_hp
		self.unit_type = '朋友'

	def info(self):
		print('我是一名骑士')

	def acquire_hut(self, hut):
		print_bold('进入%d号木屋' % hut.number, end='')
		is_enemy = (isinstance(hut.occupant, AbstractGameUnit) and hut.occupant.unit_type == '敌人')
		continue_attack = 'y'
		if is_enemy:
			print_bold('发现敌人!')
			self.show_health(bold=True, end='  ')
			hut.occupant.show_health(bold=True, end='')
			while continue_attack:
				try:
					continue_attack = input('是否继续攻击?(y/n): ')
					assert continue_attack=='y' or continue_attack=='n'    #处理用户输出不是y/n的情况
					if continue_attack == 'n':
						self.runaway()
						break
					self.attack(hut.occupant)
					if hut.occupant.health_meter <= 0:
						print(' ')
						hut.acquire(self)
						break
					if self.health_meter <= 0:
						print(' ')
						break
				except AssertionError:
					print('错误!请输入y或者n')
					continue
		else:
			if hut.get_occupant_type() == '无人居住':
				print_bold('木屋无人居住')
			else:
				print_bold('找到一个朋友')
			hut.acquire(self)
			self.heal()

	def runaway(self):
		print_bold('溜了溜了...')
		self.enemy = None


class hut():
	def __init__(self, number, occupant):
		self.occupant = occupant
		self.number = number
		self.is_acquired = False  # 木屋是否被调查

	def acquire(self, new_occupant):
		self.occupant = new_occupant
		self.is_acquired = True
		print_bold('干得好!,%d号木屋已经被调查' % self.number)

	def get_occupant_type(self):
		if self.is_acquired:
			occupant_type = '已被调查'
		elif self.occupant is None:
			occupant_type = '无人居住'
		else:
			occupant_type = self.occupant.unit_type
		return occupant_type


class orcrider(AbstractGameUnit):
	def __init__(self, name=''):
		super().__init__(name=name)
		self.max_hp = 30
		self.health_meter = self.max_hp
		self.unit_type = '敌人'
		self.hut_number = 0

	def info(self):
		print('啊啊啊啊!兽人永不为奴!别惹我')


class attackoftheorcs:
	def __init__(self):
		self.huts = []
		self.player = None

	def get_occupants(self):
		return [x.get_occupant_type() for x in self.huts]

	def show_game_mission(self):
		print_bold('任务1:')
		print('1、打败所有敌人')
		print('2、调查所有木屋,获取所有木屋的控制权')
		print_dotted_line()

	def process_user_choice(self):
		verifying_choice = True
		idx = 0
		print("木屋调查情况: %s" % self.get_occupants())
		while verifying_choice:
			user_choice = input('选择一个木屋进入(1-5):')
			try:
				idx=int(user_choice)
			except ValueError as e :   #处理用户输出不是数字的异常
				print('非法输入,错误信息:%s\n'%e.args)
				continue
			try:     #处理用户输出小于等于0的异常
				idx=int(user_choice)
				assert idx>0
			except AssertionError:
				print('错误!请输入大于0的数值')
				continue
			try:
				if self.huts[idx-1].is_acquired:
					print ('你已经调察过这个木屋,请再尝试一次'
					  '提示:不能再已调查的木屋得到治疗')
				else:
					verifying_choice=False
			except IndexError:     #处理用户输入数值超过范围的异常
				print('无效的输入:',idx)
				print('输出的数字范围必须在0~5之间,请在尝试一次')
				continue
		return idx

	def occupy_huts(self):
		for i in range(5):
			choice_list = ['敌人', '朋友', None]
			computer_choice = random.choice(choice_list)
			if computer_choice == '敌人':
				name = '敌人-' + str(i + 1)
				self.huts.append(hut(i + 1, orcrider(name)))
			elif computer_choice == '朋友':
				name = '骑士-' + str(i + 1)
				self.huts.append(hut(i + 1, knight(name)))
			else:
				self.huts.append(hut(i + 1, computer_choice))

	def play(self):
		self.player = knight()
		self.occupy_huts()
		acquires_hut_counter = 0
		self.show_game_mission()
		self.player.show_health(bold=True)
		while acquires_hut_counter < 5:
			idx = self.process_user_choice()
			self.player.acquire_hut(self.huts[idx - 1])
			if self.player.health_meter <= 0:
				print_bold('你输了!祝下次好运!')
				break
			if self.huts[idx - 1].is_acquired:
				acquires_hut_counter += 1
		if acquires_hut_counter == 5:
			print_bold('祝贺你!,你已经调查完所有的木屋')


if __name__ == '__main__':
	game = attackoftheorcs()
	game.play()

2.4 自定义异常

自定义一个gameuniterror异常类:

class Gameuniterror(Exception):
	def __init__(self,message='',code=000):
		super().__init__(message)
		self.error_message='~'*50+'\n'
		self.error_dict={
			000:'error-000:unspecified error',
			101:'error-101:health meter problem',
			102:'error-102:attack issue ignored'
		}
		try:
			self.error_message+=self.error_dict[code]
		except KeyError:
			self.error_message+=self.error_dict[000]
		self.error_message+='\n'+'~'*50

这里我们定义了一个gameuniterror异常类,继承自Exception类。该异常类可以传输参数code,用来表征不同种类的异常。
在gameunit类中定义的治疗heal函数我们做出如下的修改:

from gameuniterror import GameUnitError
def heal(self, heal_by=100, full_healing=False):
		if self.health_meter == self.max_hp:
			return
		if full_healing:
			self.health_meter = self.max_hp
		else:
			self.health_meter += heal_by
		if self.health_meter>self.max_hp:
			raise Gameuniterror(message='health_meter>max_hp',code=101)
		print_bold("你已经被治疗!", end=' ')
		self.show_health(bold=True)

新建一个.py文件:

from attackoforchs import knight
from gameuniterror import Gameuniterror
if __name__=='__main__':
	print ('--------------')
	knight=knight('sir ball')
	knight.health_meter=10
	knight.show_health()
	try:
		knight.heal(heal_by=100)
	except Gameuniterror as e:
		print (e)  #注意这里输出的是message参数
		print(e.error_message)#这里输出的是error_message参数

输出结果:
Health: sir ball: 10
health_meter>max_hp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error-101:health meter problem
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
print (e)输出的是在基类Exception中初始化的message参数,如果没有进行初始化,则不输出任何东西。
如果异常种类较多,有两种处理方法:一是增加error.dict的关键字,二是通过创建以gameuniterror为父类的子类,该子类可以只表示某一种特定的异常。

参考:https://www.cnblogs.com/jessonluo/p/4743574.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值