一、异常处理机制
Python的异常处理机制是用来捕捉和处理程序中的错误或异常情况,以确保程序在出现问题时能够优雅地退出或做出相应的处理,而不是直接崩溃。Python的异常处理是通过 try
、except
、else
和 finally
语句来实现的。
1.1 异常(Exception)
在Python中,异常是指在程序执行过程中发生的错误或意外情况。这些错误通常会导致程序中断或不正常终止,除非我们用适当的机制进行处理。
1.2 基本的异常处理结构
try
和except
最基本的异常处理结构包含 try
和 except
块。当Python代码运行时,如果在 try
块中出现了异常,程序会跳到与之匹配的 except
块来处理这个异常,而不会直接崩溃。
try:# 可能发生异常的代码result = 10 / 0 # 除以0会抛出ZeroDivisionError
except ZeroDivisionError:# 捕捉到ZeroDivisionError时执行的代码print("不能除以零!")
解释:
try
块:放置可能会抛出异常的代码。except
块:当try
块中的代码抛出异常时,会跳到对应的except
块来处理异常。- 在这个例子中,
10 / 0
会抛出ZeroDivisionError
,因此程序会执行except
块中的print("不能除以零!")
。
1.3 处理多个异常
一个 try
块可以有多个 except
块,用来捕获不同类型的异常。如果捕获的异常类型与发生的异常匹配,则会执行相应的 except
块。
try:# 可能发生不同异常的代码result = int("abc") # 会抛出ValueError
except ZeroDivisionError:print("不能除以零!")
except ValueError:print("无效的数字!")
except Exception as e:print(f"发生了未知的异常:{e}")
解释:
- 在这个例子中,
int("abc")
会抛出ValueError
异常,所以程序会进入except ValueError
块并打印"无效的数字!"
。 except Exception as e
是一个通用的异常捕获块,可以捕捉所有类型的异常,并通过e
访问异常的详细信息。
1.4 else
块
else
块用于在没有发生异常时执行的代码。如果 try
块中的代码没有抛出任何异常,则会执行 else
块中的内容。
try:result = 10 / 2 # 正常情况,不会抛出异常
except ZeroDivisionError:print("不能除以零!")
else:print("计算成功,结果是:", result)
解释:
- 由于
10 / 2
不会抛出异常,程序会跳到else
块并打印"计算成功,结果是: 5.0"
。
1.5 finally
块
finally
块中的代码无论是否发生异常都会被执行。它通常用于清理操作,比如关闭文件、释放资源等。
try:file = open("file.txt", "r")content = file.read()
except FileNotFoundError:print("文件未找到!")
finally:print("文件处理完成!")file.close() # 确保文件被关闭
解释:
- 即使
open("file.txt", "r")
抛出FileNotFoundError
异常,finally
块中的file.close()
也会被执行,确保资源得到清理。
1.6 捕获所有异常
如果你不确定会抛出什么类型的异常,可以使用 except
后不指定异常类型来捕获所有异常。
try:result = 10 / 0 # 除以0会抛出异常
except Exception as e:print(f"发生了一个异常:{e}")
解释:
Exception
是所有异常的基类,它会捕获所有的异常类型。
1.7 抛出异常(raise
)
你可以在程序中手动抛出异常,通常用于在某些条件下主动通知程序错误的发生。
def divide(a, b):if b == 0:raise ValueError("除数不能为零!")return a / btry:result = divide(10, 0)
except ValueError as e:print(f"错误:{e}")
解释:
raise ValueError("除数不能为零!")
用来手动抛出ValueError
异常。
1.8 自定义异常
你还可以定义自己的异常类,这样可以在遇到特定的业务逻辑错误时进行更精确的捕获。
class MyError(Exception):def __init__(self, message):self.message = messagesuper().__init__(self.message)try:raise MyError("这是一个自定义的错误!")
except MyError as e:print(f"捕获到自定义错误:{e}")
解释:
- 通过继承
Exception
类,你可以创建一个自定义的异常类型,在捕获异常时可以用来表示特定的错误。
1.9 python日志
日志是用来记录程序运行状态、调试信息、错误信息等的工具。日志能够帮助开发者在程序运行时实时追踪和记录信息,便于后期问题的定位与分析。Python 提供了 logging
模块来进行日志的记录。
1.9.1 使用 logging
模块
logging
模块是 Python 标准库中用于日志记录的工具,具有非常强大的功能。它可以将日志信息输出到控制台、文件或其他外部系统。
1.9.2 日志基本配置
import logging# 设置日志级别和日志输出格式
logging.basicConfig(level=logging.DEBUG, # 设置最低日志级别为 DEBUGformat='%(asctime)s - %(levelname)s - %(message)s', # 日志格式filename='app.log', # 设置日志文件名filemode='w' # 设置文件打开模式('w' 会覆盖原文件,'a' 会追加)
)# 输出不同级别的日志信息
logging.debug("这是 debug 信息")
logging.info("这是 info 信息")
logging.warning("这是 warning 信息")
logging.error("这是 error 信息")
logging.critical("这是 critical 信息")
日志级别(level): logging
模块提供了五种日志级别,用于设置日志的严重程度,按照严重程度从低到高依次为:
DEBUG
:最详细的信息,用于调试。INFO
:一般的信息,表示程序的正常运行状态。WARNING
:警告信息,表示程序的潜在问题。ERROR
:错误信息,表示程序出现了某些问题,但程序依然能够继续运行。CRITICAL
:严重错误,表示程序遇到了严重问题,可能会导致程序崩溃。
在使用 logging.basicConfig()
时,可以通过 level
参数来设置最低的日志级别。日志级别为 DEBUG
时,会记录所有级别的信息;如果设置为 WARNING
,则只记录 WARNING
及以上级别的日志。
1.9.3 日志输出到不同位置
日志不仅可以输出到控制台,还可以输出到文件、外部系统等。可以通过 handlers
配置不同的输出位置。
import logging# 设置日志记录器
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)# 创建控制台输出 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)# 创建文件输出 handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)# 设置日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)# 添加 handler 到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)# 输出日志
logger.debug("Debug 信息")
logger.info("Info 信息")
logger.warning("Warning 信息")
logger.error("Error 信息")
logger.critical("Critical 信息")
在这个示例中,我们创建了两个 Handler
,分别将日志输出到控制台和文件。
总结
Python的异常处理机制非常强大,可以帮助你更好地控制程序的错误处理,避免程序崩溃。它包括:
try
/except
用于捕获异常并处理错误。else
用于在没有异常时执行代码。finally
用于清理操作。raise
用于手动抛出异常。- 自定义异常类可以帮助处理特定类型的错误。
练习题
1. 编写一个函数,接受一个字符串 s 作为参数,如果 s 中包含非数字字符, 则抛出一个自定义的异常“NonNumericError”
class MyException(Exception):def __init__(self, msg):self.msg = msgdef __str__(self):return self.msg
def check(s):for i in s:if not i.isdigit():raise MyException("NonNumericError")return Trues1="123abc"
s2="123"try:check(s1)
except MyException as e:print(e)
else:print("No error")
try:check(s2)
except MyException as e:print(e)
else:print("No error")
2. 编写一个函数,接受两个参数 a 和 b,其中 a 和 b 都是数字 要求: 返回 a/b 的结果。如果 b 为 0 , 则抛出一个自定义的异常“ZeroDivisionError”。
class MyException(Exception):def __init__(self, msg):self.msg = msgdef __str__(self):return self.msg
def div(a, b):if b == 0:raise MyException("ZeroDivisionError")return a/b
try:print(div(1, 0))
except MyException as e:print(e)
3. 编写一个函数,接受一个数字 n 作为参数 要求: 如果 n 小于 0, 则抛出一个自定义的异常“NegativeNumberError”。
class MyException(Exception):def __init__(self, msg):self.msg = msgdef __str__(self):return self.msg
def neg(n):if n < 0:raise MyException("NegativeNumberError")return n
try:print(neg(-1))
except MyException as e:print(e)
4. 编写一个函数,接受一个字符串 s 作为参数 要求: 如果 s 的长度小于 10, 则抛出一个自定义的异常“StringLengthError”。
class MyException(Exception):def __init__(self, msg):self.msg = msgdef __str__(self):return self.msg
def strl(s):if len(s) < 10:raise MyException("StringLengthError")return s
try:print(strl("1234567890"))
except MyException as e:print(e)
5. 编写一个函数,接受一个数字 n 作为参数 要求:如果 n 不是偶数,则抛出一个自定义的异常“EvenNumberError”。
class MyException(Exception):def __init__(self, msg):self.msg = msgdef __str__(self):return self.msg
def even(n):if n % 2 != 0:raise MyException("EvenNumberError")return n
try:print(even(1))
except MyException as e:print(e)
二、文件操作
2.1 文件操作的重要性和应用场景
重要性
-
- 数据持久化
- 跨平台兼容性
- 数据备份与恢复
- 数据共享
- 配置管理
- 日志记录
应用场景
-
- 数据分析
- web开发
- 文本处理
2.2 文件的基本概念
2.2.1 文件的概念
文件是一个存储在某种持久性存储介质【硬盘、光盘、磁盘等】上的数据的结合。
文件可以包含各种类型的信息:文本、图像、音频、视频、应用程序代码以及其他类型的二进制数据。
文件通常由数据、元数据、文件系统等几部分组成。
文件的属性有:文件名、位置、文件类型、文件大小、创建日期、修改日期、访问权限。
2.2.2 文件的分类
windows系统下大致分为以下几种:
- 文本文件:包含可读字符的文件,如.txt .csv .py .html等
- 二进制文件:包含不可直接读的原始二进制数据的文件,如.exe .jpg .mp3等
- 可执行文件:可以被操作系统执行的文件,如.exe
- 数据文件:用于存储应用程序数据的文件,如数据库文件、配置文件等
- 目录/文件夹:用于组织和管理其他文件的特殊文件
在Linux系统下,有以下几种:
- - 普通文件
- d 目录文件
- b 块设备文件【底层驱动文件】
- c 字符设备文件【底层驱动文件】
- l 链接文件【类似于快捷方式】
- p 管道文件,用于进程间的通信
- s 套接字文件,用于网络通信的端点
文本文件:
- 文本文件是由单一特定编码组成的文件,如UTF-8编码。
- 由于存在编码,文本文件也被看成是存储着数据的长字符串。
二进制文件:
- 直接由比特0和1组成,没有统一字符编码
- 一般都存在二进制0和1的组织结构,即文件格式
2.2.3 文件位置【路径】
在计算机系统中,路径是用来表示文件位置的一种方式。
路径又分绝对路径和相对路径。
windows系统下:
绝对路径:带有盘符的或者带有网址的
C:\Users\admin\Desktop\py_gj\py_day3
相对路径:
./py_gj/py_day3
../py_gj/py_day3
这里的点有特殊含义,一个点表示当前路径下,两个点表示上一级路径下。
2.3 文件的操作
Python 提供了非常方便的工具来进行文件操作,包括打开、读取、写入、修改和关闭文件等操作。Python 中的文件操作主要是通过内置的 open()
函数来完成的。
2.3.1 打开文件 (open()
)
在 Python 中,文件的操作是通过 open()
函数来完成的,open()
用来打开一个文件,并返回一个文件对象。你可以对文件进行读写操作。常见的语法是:
file_object = open('文件路径', '模式')
文件打开模式:
open()
函数的第二个参数是文件的打开模式,它告诉 Python 你对文件进行何种操作。常见的文件打开模式有:
'r'
:只读模式(默认模式)。如果文件不存在,会抛出FileNotFoundError
。'w'
:写入模式。如果文件已存在,它会覆盖原有内容。如果文件不存在,会新建一个空文件。'a'
:追加模式。向文件末尾添加内容,如果文件不存在,则会创建文件。'b'
:二进制模式。通常与其他模式结合使用,表示文件以二进制格式打开(例如'rb'
、'wb'
等)。
2.3.2. 读取文件内容 (read()
, readline()
, readlines()
)
read()
read()
方法会读取整个文件的内容并将其作为一个字符串返回。
with open('example.txt', 'r') as file:content = file.read()print(content)
read()
会读取文件中的所有内容。如果文件较大,可能会占用较多内存。- 你也可以传入一个数字参数,表示读取指定字节数的内容。例如,
file.read(100)
读取文件的前 100 个字节。
readline()
readline()
方法一次读取文件中的一行。
with open('example.txt', 'r') as file:line1 = file.readline()print(line1)
readline()
返回文件中的一行(包括换行符),如果没有更多行了,返回空字符串。
readlines()
readlines()
方法会将文件的所有行读取并返回一个列表,每一行作为列表的一个元素。
with open('example.txt', 'r') as file:lines = file.readlines()print(lines)
readlines()
读取所有行,并将每行作为列表中的一个元素(包含换行符)。
2.3.3 写入文件内容 (write()
, writelines()
)
write()
write()
方法将字符串写入文件。如果文件不存在,它会创建一个新的文件。如果文件存在,它会覆盖文件的内容。
with open('example.txt', 'w') as file:file.write("Hello, Python!\n")file.write("This is a test.")
- 如果你希望写入多行内容,可以通过
write()
多次调用来写入每一行。
writelines()
writelines()
方法接受一个可迭代对象(如列表),将其每个元素写入文件中。注意,它不会自动加上换行符,除非你显式地在每行末尾加上换行符。
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open('example.txt', 'w') as file:file.writelines(lines)
2.3.4 文件指针的移动
Python 中的文件对象是基于流的,文件读
file = open('example.txt', 'r')
# 文件操作
file.close()
取是从文件指针当前位置开始的。你可以使用以下方法移动文件指针:
seek()
seek(offset, whence)
用来设置文件指针的位置。
offset
:文件指针的偏移量。whence
:偏移的起始位置,默认是os.SEEK_SET
,即从文件的开始位置起偏移。
with open('example.txt', 'r') as file:file.seek(5) # 将文件指针移到第5个字节print(file.read()) # 从第5个字节开始读取
2.3.5 关闭文件 (close()
)
在文件操作完成后,应该使用 close()
方法来关闭文件,释放系统资源。
file = open('example.txt', 'r')
# 文件操作
file.close()
建议: 在 Python 中,最好使用 with
语句来自动管理文件的打开和关闭。with
语句会在代码块执行完毕后自动关闭文件,确保文件不会长时间占用资源。
with open('example.txt', 'r') as file:content = file.read()print(content)
# 文件会自动关闭
2.3.6 上下文管理器 with
with
语句是 Python 提供的一种文件处理方式,可以自动管理文件的打开和关闭,避免忘记关闭文件或在异常发生时未能关闭文件。
with open('example.txt', 'w') as file:file.write("Hello, World!")
# 这里文件已经自动关闭,不需要手动调用 file.close()
2.3.7 文件读取/写入的异常处理
在进行文件操作时,我们可能会遇到一些常见的异常,例如文件不存在、权限问题等。你可以通过异常处理机制来捕获这些错误。
try:with open('non_existent_file.txt', 'r') as file:content = file.read()
except FileNotFoundError:print("文件未找到!")
except IOError as e:print(f"发生了IO错误: {e}")
2.3.8 文件的其他操作
除了基本的读取、写入和关闭操作,Python 还提供了许多关于文件操作的其他功能,常见的有:
os.path.exists()
:检查文件或目录是否存在。os.remove()
:删除文件。os.rename()
:重命名文件。os.makedirs()
:创建多层目录。
import os# 检查文件是否存在
if os.path.exists('example.txt'):print("文件存在")
else:print("文件不存在")# 删除文件
os.remove('example.txt')# 创建目录
os.makedirs('new_folder', exist_ok=True)
总结
Python 文件操作非常灵活和强大,常用的文件操作包括:
- 使用
open()
打开文件并指定操作模式。 - 通过
read()
、readline()
或readlines()
读取文件内容。 - 使用
write()
和writelines()
写入文件。 seek()
和tell()
控制文件指针位置。- 使用
close()
关闭文件,或者使用with
语句自动管理文件打开和关闭。 - 捕获文件操作中的异常以避免程序崩溃。
练习题
(一)将一个字符串写入二进制文件中 binary.txt。
要求:
1、这将创建一个名为 binary.txt 的二进制文件
2、并将字符串 Hello, world! 写入其中。
a="Hello, world!"
with open("./binary.txt",mode='wb') as f:f.write(a.encode())
(二)从上面的二进制文件中读取一个字节并将其打印出来。 要求: 1、这将打印出二进制文件的第一个字节。
try:with open("./binary.txt",mode='rb') as f:print(f.read(1))
except Exception as e:print(e)
(三)将 1-100 之间能被 5 整除的数保存在列表 numbers 中
1. 将 numbers 中的元素写入到文件 num.txt 中
2. 使用文件操作方式打开 num.txt 文件读取内容,并计算它们的平均数
numbers = [i for i in range(1,101) if i%5==0]
with open("./num.txt",mode='w') as f:for i in numbers:f.write(str(i)+'\n')with open("./num.txt",mode='r') as f:sum=0for i in f.readlines():sum+=int(i)print(sum/len(numbers))