《Python 基础篇》四:异常处理和文件操作

Author: ACatSmiling

Since: 2024-09-27

异常

程序在运行过程当中,不可避免的会出现一些错误,比如:使用了没有赋值过的变量,使用了不存在的索引,除 0 等。这些错误在程序中,我们称其为异常

  • 程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行!

处理异常

程序运行时出现异常,目的并不是让我们的程序直接终止!Python 是希望在出现异常时,我们可以编写代码来对异常进行处理!

try-except-finally 语句

try:
代码块(可能出现错误的语句)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
else:
代码块(没出错时要执行的语句)
finally:
代码块(该代码块总会执行)
  • try 是必须的,else 语句有没有都行,except 和 finally 至少有一个。

  • 可以将可能出错的代码放入到 try 语句,这样如果代码没有错误,则会正常执行,如果出现错误,则会执行 expect 子句中的代码,这样我们就可以通过代码来处理异常,避免因为一个异常导致整个程序的终止。

异常的传播

当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播,如果函数中没有对异常进行处理,则异常会继续向函数调用处传播,如果函数调用处处理了异常,则不再传播,如果没有处理则继续向调用处传播,直到传递到全局作用域(主模块),如果依然没有处理,则程序终止,并且显示异常信息。

当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的异常对象中,而异常传播时,实际上就是异常对象抛给了调用处。

示例:

def fn():
print('Hello fn')
print(a)
print(10 / 0)
def fn2():
print('Hello fn2')
fn()
def fn3():
print('Hello fn3')
fn2()
fn3()

异常对象

Python 中常见的异常类型:

报错类型 描述
AssertionError 当 assert 断言条件为假的时候抛出的异常
AttributeError 当访问的对象属性不存在的时候抛出的异常
IndexError 超出对象索引的范围时抛出的异常
KeyError 在字典中查找一个不存在的 key 抛出的异常
NameError 访问一个不存在的变量时抛出的异常
OSError 操作系统产生的异常
SyntaxError 语法错误时会抛出此异常
TypeError 类型错误,通常是不同类型之间的操作会出现此异常
ZeroDivisionError 进行数学运算时除数为 0 时会出现此异常

示例:

print('异常出现前')
l = []
try:
# print(c)
# l[10]
# 1 + 'hello'
print(10 / 0)
except NameError:
# 如果 except 后不跟任何的内容,则此时它会捕获到所有的异常
# 如果在 except 后跟着一个异常的类型,那么此时它只会捕获该类型的异常
print('出现 NameError 异常')
except ZeroDivisionError:
print('出现 ZeroDivisionError 异常')
except IndexError:
print('出现 IndexError 异常')
# Exception 是所有异常类的父类,所以如果 except 后跟的是 Exception,它也会捕获到所有的异常
# 可以在异常类后边跟着一个 as xx 此时 xx 就是异常对象
except Exception as e: # 等同于 except:
print('未知异常', e, type(e))
finally:
print('无论是否出现异常,该子句都会执行')
print('异常出现后')

抛出异常

可以使用raise 语句来手动抛出异常,raise 语句后需要跟一个异常类或异常的实例。

示例:

# 也可以自定义异常类,只需要创建一个类继承 Exception 即可
class MyError(Exception):
pass
def add(a, b):
# 如果 a 和 b 中有负数,就向调用处抛出异常
if a < 0 or b < 0:
# raise 用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
# raise ExceptioUS20190040060A1-20190207.XMLn
# 抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
# raise Exception('两个参数中不能有负数!')
raise MyError('自定义的异常')
# 也可以通过 if else 来代替异常的处理,但无法考虑到所有可能出异常的条件,难以控制
return a + b
print(add(-123, 456))

文件

文件是以计算机硬盘为载体存储在计算机上的信息集合,文件可以是文本文档、图片、程序等等。计算机文件基本上分为二种:二进制文件和纯文本文件。

  • 二进制文件没有统一的字符编码。与文本文件的一个最主要的区别在于是否有统一的字符编码格式,二进制文件顾名思义是直接由 0 与 1 组成,无统一的字符编码,例如图片文件(jpg、png),视频文件(avi)等。
  • 纯文本文件有统一的编码,可以被看做存储在磁盘上的长字符串。编码格式常见的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。

通过 Python 程序,操作文件的步骤:

  • 打开文件。
  • 对文件进行各种操作(读、写),然后保存。
  • 关闭文件。

打开文件

open():打开一个文件。

语法:

open(file, mode='r', buffering=-1, encoding_=None, errors=None, newline=None, closefd=True, opener=None)
  • file:要打开的文件的名字(路径)。

  • mode:打开文件的模式。

    mode 解释
    r 只读,默认模式,文件的指针将会放在文件的开头。文件必须存在,不存在则抛出异常
    w 只写,文件的指针将会放在文件的开头,写之前会清空文件的内容。如果文件不存在,会创建新文件
    a 追加的方式,文件的指针将会放在文件的结尾,在原本内容中继续写。如果文件不存在,则会创建新文件
    r+ 可读可写
    w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写
    b rb、wb、ab、rb+、wb+、ab+ 意义和上面一样,用于二进制文件操作,例如图片、音频、视频等
  • 返回值:返回一个对象,这个对象就代表了当前打开的文件。

示例:

# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
file_name = 'demo.txt'
# 在 Windows 系统使用路径时,可以使用 / 来代替 \
# 或者可以使用 \\ 来代替 \
# 或者也可以使用原始字符串
# file_name = 'hello\\demo.txt'
# file_name = r'hello\demo.txt'
# 表示路径,可以使用 .. 来返回一级目录
# file_name = '../hello/demo.txt'
# 如果目标文件距离当前文件比较远,此时可以使用绝对路径
# 绝对路径应该从磁盘的根目录开始书写
# file_name = r'C:\Users\XiSun\Desktop\hello.txt'
file_obj = open(file_name) # 打开 file_name 对应的文件
print(file_obj) # <_io.TextIOWrapper name='demo.txt' mode='r' encoding='cp936'>

关闭文件

# 打开文件
file_name = 'demo.txt'
# 调用 open() 来打开文件
# file_obj = open(file_name)
# 当我们获取了文件对象以后,所有的对文件的操作都应该通过对象来进行
# 读取文件中的内容:
# read() 方法,用来读取文件中的内容,它会将内容全部保存为一个字符串返回
# content = file_obj.read()
# print(content)
# 关闭文件:
# 调用 close() 方法来关闭文件
# file_obj.close()
# 上面的 open() 和 close() 是常规写法,可以用下面的方式简化操作
# with ... as 语句:
with open(file_name) as file_obj:
# 在 with 语句中可以直接使用 file_obj 来做文件操作
# 此时这个文件只能在 with 中使用,一旦 with 结束则文件会自动 close()
print(file_obj.read())
# 进一步完善,添加文件读取异常处理
# 文件处理的标准格式:
file_name = 'hello.txt'
try:
with open(file_name) as file_obj:
print(file_obj.read())
except FileNotFoundError:
print(f'{file_name} 文件不存在~~')

读取文件

file_name = 'demo.txt'
try:
# 调用 open() 来打开一个文件,可以将文件分成两种类型
# 一种,是纯文本文件(使用 utf-8 等编码编写的文本文件)
# 一种,是二进制文件(图片、mp3、ppt 等这些文件)
# open() 打开文件时,默认是以文本文件的形式打开的,但是 open() 默认的编码为 None
# 所以处理文本文件时,必须要指定文件的编码
with open(file_name, encoding='utf-8') as file_obj:
# 首先,判断文件是否可读,增加容错
print(file_obj.readable()) # True
# 通过 read() 来读取文件中的内容
# 如果直接调用 read() 它会将文本文件的所有内容全部都读取出来
# 如果要读取的文件较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄漏
# 所以对于较大的文件,不要直接调用 read()
# help(file_obj.read) 查看帮助信息
# read() 可以接收一个 size 作为参数,该参数用来指定要读取的字符的数量
# 默认值为 -1,它会读取文件中的所有字符
# 可以为 size 指定一个值,这样 read() 会读取指定数量的字符,
# 每一次读取都是从上次读取到位置开始读取的
# 如果字符的数量小于 size,则会读取剩余所有的
# 如果已经读取到了文件的最后了,则会返回''空串
# content = file_obj.read(-1)
content = file_obj.read(6)
content = file_obj.read(6)
content = file_obj.read(6)
content = file_obj.read(6)
# print(content)
# print(len(content))
except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')
# 读取大文件的方式
file_name = 'demo.txt'
try:
with open(file_name, encoding='utf-8') as file_obj:
# 定义一个变量,来保存文件的内容
file_content = ''
# 定义一个变量,来指定每次读取的大小
chunk = 100
# 创建一个循环来读取文件内容
while True:
# 读取 chunk 大小的内容
content = file_obj.read(chunk)
# 检查是否读取到了内容
if not content:
# 内容读取完毕,退出循环
break
# 输出内容
# print(content, end='')
file_content += content
except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')
print(file_content)
import pprint
file_name = 'demo.txt'
try:
with open(file_name, encoding='utf-8') as file_obj:
# readline():
# 该方法可以用来读取一行内容
# print(file_obj.readline(), end='') # print() 打印自带换行符
# readlines():
# 该方法用于一行一行的读取内容,它会一次性将读取到的内容封装到一个列表中返回
# r = file_obj.readlines()
# pprint.pprint(r[0]) # 'aaa\n'
# pprint.pprint(r[1]) # 'bbb\n'
# pprint.pprint(r[2]) # 'ccc\n'
# file_obj 是一个可迭代对象
print(isinstance(file_obj, collections.abc.Iterator)) # True
# 简化写法,遍历 file_obj,也可以遍历 readlines() 方法返回的行列表
for t in file_obj:
print(t, end='')
except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')

写入文件

file_name = 'demo1.txt'
# 使用 open() 打开文件时必须要指定打开文件所要做的操作:读、写、追加
# 如果不指定操作类型,则默认是 `读取文件` ,而读取文件时是不能向文件中写入的
# r:表示只读的
# w:表示是可写的,使用 w 来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件
# 截断文件指删除原来文件中的所有内容(覆盖原文件内容)
# a:表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
# x:用来新建文件,如果文件不存在则创建,存在则报错
# +:为操作符增加功能
# r+:即可读又可写,文件不存在会报错
# w+:即可写又可读
# a+:即可追加又可读
# with open(file_name , 'w' , encoding='utf-8') as file_obj:
# with open(file_name , 'r+' , encoding='utf-8') as file_obj:
with open(file_name, 'x', encoding='utf-8') as file_obj:
# 首先,判断文件是否可读,增加容错
print(file_obj.writable()) # True
# write() 来向文件中写入内容
# 如果操作的是一个文本文件的话,则 write() 需要传递一个字符串作为参数
# 该方法会可以分多次向文件中写入内容
# 写入完成以后,该方法会返回写入的字符的个数
file_obj.write('aaa\n')
file_obj.write('bbb\n')
file_obj.write('ccc\n')
file_obj.write(str(123) + '123123\n') # 要转换为字符串,且不会自动换行
r = file_obj.write('今天天气真不错')
print(r) # 7

二进制文件操作

file_name = 'F:/QQMusic/FLOW - Go!!!.flac'
# 读取模式:
# t:读取文本文件(默认值)
# b:读取二进制文件
with open(file_name, 'rb') as file_obj:
# 读取文本文件时,size 是以字符为单位的
# 读取二进制文件时,size 是以字节为单位
# print(file_obj.read(100))
# 将读取到的内容写出来
# 定义一个新的文件
new_name = 'aa.flac'
with open(new_name, 'wb') as new_obj:
# 定义每次读取的大小
chunk = 1024 * 100 # 100 KB
while True:
# 从已有的对象中读取数据
content = file_obj.read(chunk)
# 内容读取完毕,终止循环
if not content:
break
# 将读取到的数据写入到新对象中
new_obj.write(content)

seek() 和 tell()

# 二进制文件
with open('demo.txt', 'rb') as file_obj:
# print(file_obj.read(30))
# print(file_obj.read(100))
# tell() 方法用来查看当前读取的位置
print('当前读取到了 -->', file_obj.tell())
# seek() 方法可以修改当前读取的位置
# seek() 需要两个参数:
# 第一个:要切换到的位置
# 第二个:计算位置的方式
# 可选值:
# 0 从头计算,默认值
# 1 从当前位置计算
# 2 从最后位置开始计算
# file_obj.seek(55)
# file_obj.seek(80, 0)
# file_obj.seek(70, 1)
file_obj.seek(-10, 2) # 从文件的最后往前读 10 个字符
print(file_obj.read())
# 文本文件
with open('demo.txt', 'rt', encoding='utf-8') as file_obj:
print('当前读取到了 -->', file_obj.tell())
file_obj.seek(9) # 从头开始计算,切换到第 9 个字节开始读取
print(file_obj.read())
  • 文本文件的操作模式下,seek() 方法的第二个参数只能写 0。
  • 如果希望 seek() 方法的第二个蚕食是 1 或者 2,必须在二进制文件的操作模式下。

文件的其他操作

import os
from pprint import pprint
# os.listdir():获取指定目录的目录结构
# 需要一个路径作为参数,会获取到该路径下的目录结构,默认路径为 . ,即当前目录
# 该方法会返回一个列表,目录中的每一个文件(夹)的名字都是列表中的一个元素
r = os.listdir()
pprint(r) # ['.idea', 'aa.flac', 'demo.txt', 'hello', 'main.py', '__pycache__']
# os.getcwd():获取当前所在的目录
r = os.getcwd()
pprint(r) # 'D:\\JetBrainsWorkSpace\\PycharmProjects'
# os.chdir():切换当前所在的目录 作用相当于cd命令
# os.chdir('c:/')
# r = os.getcwd()
# pprint(r) # 'c:\\'
# os.mkdir();创建目录
os.mkdir("aaa") # 在当前目录下创建一个名字为 aaa 的目录,如果已存在,则报错
# os.rmdir():删除目录
os.rmdir('aaa') # 在当前目录下删除一个名字为 aaa 的目录,如果不存在,则报错
# open();打开文件
open('aa.txt', 'w')
# os.remove():删除文件
os.remove('aa.flac')
# os.rename('旧名字','新名字'):可以对一个文件进行重命名,也可以用来移动一个文件
os.rename('hello', 'bb.txt')
# os.rename('bb.txt', 'c:/users/lilichao/desktop/bb.txt')
pprint(r)

csv 文件

import csv, random
from my_package import my_tools
lista = []
def random_info(n=100):
subjects = ['python', 'java', 'C++', 'html']
names = []
for i in range(n // len(subjects)):
name = my_tools.random_string(random.randint(3, 6))
names.append(name)
for i in range(n):
subject = random.choice(subjects)
score = random.randint(50, 100)
name = random.choice(names)
for j in lista:
if j[0] == name and j[1] == subject:
break
else:
lista.append([name, subject, score])
def average():
with open('data.csv', mode='r', encoding='utf-8') as f:
cf = csv.reader(f)
head = next(cf) # 获取表头
scores = []
for i in cf:
scores.append(int(i[2]))
return sum(scores) / len(scores)
def make_datas():
with open('data.csv', mode='a', encoding='utf-8') as f:
cf = csv.writer(f)
random_info()
cf.writerows(lista)
make_datas()
# result = average()
# print('大家的平均分是',round(result,2))

data.csv:

hTRI,java,83
kjRfoP,python,94
KhXf,python,56
VAytb,python,77
QXvn,python,99
jGNTql,html,72
JDkeB,C++,54
mWumj,html,87
ZqeOSv,java,63
RPV,java,65
FAug,python,88
kjRfoP,C++,54
KhXf,python,74
hTRI,java,96
dJruAL,python,94
hTRI,html,85
jGNTql,html,80
KBsbou,C++,83
nmwWP,python,53
zDPclC,java,62
YRhNw,html,83
gVw,python,72
KhXf,python,63
FAug,C++,86
KhXf,C++,54
ifNLzB,java,84
qOl,java,51
vWBbN,C++,64
LCF,html,91
ZqeOSv,html,64
RPV,C++,87
RPV,html,53
vWBbN,python,87
YRhNw,java,52
FAug,python,78
gVw,html,88
LCF,java,63
lfL,html,65
FAug,html,61
KBsbou,html,89
ifNLzB,C++,69
vWBbN,java,61
hTRI,python,54
QXvn,java,55
rIgeHN,C++,59
qOl,java,51
ijIB,java,92
LCF,python,67
YRhNw,java,55
YRhNw,html,66
JDkeB,python,77
YRhNw,python,69
LCF,C++,66
qOl,java,84
hTRI,html,60
ZqeOSv,html,78
lfL,python,55
KhXf,C++,83
nmwWP,html,57
gVw,java,100
VAytb,C++,74
lfL,C++,90
VAytb,C++,66
nmwWP,html,84
rIgeHN,C++,84
lfL,html,65
mWumj,python,62
LCF,html,70
Vgd,python,52
ZqeOSv,java,67
dJruAL,java,66
gVw,python,66
VAytb,java,76
ifNLzB,python,55
YRhNw,java,98
qOl,html,66
RPV,html,61
jGNTql,html,90
qOl,python,60
lfL,html,54
dJruAL,java,94
FAug,C++,84
KhXf,html,90
LCF,C++,96
gVw,C++,70
dJruAL,java,54
QXvn,java,50
kjRfoP,python,88
ifNLzB,python,80
ZqeOSv,C++,100
zDPclC,C++,81
mWumj,html,86
LCF,java,85
VAytb,C++,77
mWumj,html,74
nmwWP,java,92
LCF,C++,78
rIgeHN,html,94
mWumj,java,77
vWBbN,html,66

原文链接

https://github.com/ACatSmiling/zero-to-zero/blob/main/PythonLanguage/python.md

posted @   ACatSmiling  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示