异常机制(Exception)
异常机制本质: 当程序出现异常, 程序安全的退出, 处理完后继续执行的机制
Python中, 引进了很多用来描述和处理异常的类, 称为异常类, 异常类定义中包含了该类异常的信息和对异常处理的方法
BaseException(所有异常的父类) > Exception > NameError, ValueError, AttributeError等
Python中一切都是对象, 异常也采用对象的方式处理, 以下为处理过程:
- 抛出异常: 在执行一个方法时, 如果发生异常, 则这个方法会生成代表该异常的一个对象, 停止当前执行路径, 并把异常对象提交给解释器
- 捕获异常: 解释器得到该异常后, 寻找相应的代码来处理异常
try-except结构
try:
# 被监控的可能引发异常的语句块
except BaseException as e:
print(e)
# 异常处理语句块
try-多个except结构
try:
# 被监控的可能引发异常的语句块
except Exception1:
# 异常Exception1的语句块
except Exception2:
# 异常Exception2的语句块
except BaseException:
# 异常BaseException的语句块
try-except-else结构
try:
# 被监控的可能引发异常的语句块
except BaseException as e:
# 异常处理语句块
else:
# 没有抛出异常时,默认执行的语句块
try-except-finally结构
finally通常用于释放try块中申请的资源
try:
# 被监控的可能引发异常的语句块
except BaseException as e:
# 异常处理语句块
finally:
# 无论是否抛出异常时,都会执行的语句块
return和异常处理问题
由于return有两种作用: 结束方法运行 和 返回值
我们一般不把return放到异常处理结构中(会有异常错误), 而是放到方法最后
常见异常
Python中的异常都派生自BaseException类
| 异常名称 | 说明 |
|---|---|
| ArithmeticError | 所有数值计算错误的基类 |
| AssertionError | 断言语句失败 |
| AttributeError | 对象没有这个属性 |
| BaseException | 所有异常的基类 |
| DeprecationWarning | 关于被弃用的特征的警告 |
| EnvironmentError | 操作系统错误的基类 |
| EOFError | 没有内建输入,达到EOF标记 |
| Exception | 常规错误的基类 |
| FloatPointError | 浮点计算错误 |
| FutureWarning | 关于构造语义将来会有改变的警告 |
| GeneratorExit | 生成器(generator)发生异常,通知退出 |
| ImportError | 导入模块/对象失败 |
| IndentationError | 缩进错误 |
| IndexError | 序列中没有此索引 |
| IOError | 输入/输出操作失败 |
| KeyboardInterrupt | 用户中断执行(通常是输入^C) |
| KeyError | 映射中没有这个键 |
| LookupError | 无效查询的基类 |
| MemoryError | 内存溢出错误(对Python解释器来说不是致命的) |
| NameError | 未声明/初始化对象(没有属性) |
| NotImplementedError | 尚未实现的方法 |
| OSError | 操作系统错误 |
| OverflowError | 数值运算超出最大限制 |
| OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
| PendingDeprecationWarning | 关于特性将会被废弃的警告 |
| ReferenceError | 弱引用(Weak Reference)试图访问已经垃圾回收了的对象 |
| RuntimeError | 一般的运行时错误 |
| RuntimeWarning | 可以的运行时行为(Runtime Behavior)的警告 |
| StandardError | 所有内建标准异常的基类 |
| StopIteration | 迭代器没有更多的值 |
| SyntaxError | Python语法错误 |
| SyntaxWarning | 可以的语法的警告 |
| SystemError | 一般的解释器系统错误 |
| SystemExit | 解释器请求退出 |
| TabError | Tab和空格键混用 |
| TypeError | 对类型无效的操作 |
| UnboundLocalError | 访问未初始化的本地变量 |
| UnicodeDecodeError | Unicode编码时的错误 |
| UnicodeError | Unicode相关的错误 |
| UnicodeTranslateError | Unicode转换时的错误 |
| UserWarning | 用户代码生成的警告 |
| ValueError | 传入无效的参数 |
| Warning | 警告的基类 |
| WindowsError | 系统调用失败 |
| ZeroDivisionError | 除(或取模)零(所有数据类型) |
with上下文管理
with: 无论有无异常, 都能释放资源
finally块由于是否发生异常都会执行, 通常我们释放资源的代码
with上下文管理, 更方便的实现释放资源的操作
with context_expr as var:
# 语句块
with上下文管理器可以自动管理资源, 在with代码块执行完毕后自动还原进入该代码之前的现场或上下文, 不论何种原因跳出with块, 不论是否有异常, 总能保证资源正常释放, 极大地简化了工作, 在文件操作和网络通信相关的场合经常使用到的
注意: with并不是取代finally, 而是在资源管理这一块用with比finally更方便
traceback模块和生成异常日志
打印异常日志演示如下:
import traceback
try:
print("step1")
num = 1 / 0
except:
with open("e:/a.log", "a") as f:
traceback.print_exc(file=f)
自定义异常类
程序开发过程中, 有时候需要自定义异常类, 自定义异常类一般是运行时异常, 通常继承Exception或者其子类即可, 命名一般为Error, Exception为后缀
自定义异常由raise语句主动抛出
代码演示:
class AgeError(Exception):
def __init__(self, errorInfo):
Exception.__init__(self)
self.errorInfo = errorInfo
def __str__(self):
return str(self.errorInfo) + " 年龄错误!应该在0-35岁之间"
if __name__ == '__main__':
age = int(input("输入一个适合的年龄:"))
if age <= 0 or age >= 35:
raise AgeError(age)
else:
print("正确的年龄", age)
#############################################################
# Traceback (most recent call last):
# File "F:\Python_Home\study\pythonProject1\1.py", line 13, in <module>
# raise AgeError(age)
# __main__.AgeError: 36 年龄错误!应该在0-35岁之间
Pycharm开发环境的调试(debug)
核心: 设置断点
跳过断点, 跳入断点, 跳出断点
文件处理
引言
一个完整的程序一般包括数据的存储和读取
在实际开发中, 需要从外部存储介质(硬盘, 光盘, U盘)读取数据, 或者将程序产生的数据存储到文件中, 实现"持久化"保存
很多软件系统是将数据存储于数据库中, 数据库实际也是基于文件形式存储的
文件类型
按文件中的数据组织形式, 可以分为文本文件和二进制文件两大类
- 文本文件: 文本文件存储的是普通"字符"文本, python默认为unicode字符集(两个字节表示一个字符, 最多可以表示: 65536个), 可以使用记事本程序打开
- 二进制文件: 二进制文件把数据内容用"字节"进行存储, 无法用记事本打开, 必须使用专用的软件解码, 常见的有: MP4视频文件, MP3音频文件, JPG图片, doc文档等等
文件操作相关模块
| 模块名称 | 功能说明 |
|---|---|
| os | 提供了与操作系统相关的功能,包括文件和目录的创建、删除以及路径操作等 |
| os.path | 提供了对文件路径的一些常用操作,如拼接路径、获取文件名和文件扩展名等 |
| shutil | 提供了对文件和目录的高级操作,例如复制、移动和删除文件等 |
| glob | 实现文件通配符匹配,用于搜索符合某种模式的文件 |
| pathlib | 提供了针对路径操作的面向对象的接口,用于更方便地操作路径和文件 |
| fileinput | 用于逐行迭代读取文件,尤其在迭代大型文件时非常有用 |
| io | 提供了用于处理文件输入和输出的工具,包括读写文件、管理缓冲和管道等 |
| codecs | 用于对文件进行编码和解码操作,特别是在处理非 ASCII 编码的文件时很有用 |
| tempfile | 用于创建临时文件和目录,在需要临时存储数据时非常有用 |
| tarfile | 提供了对 tar 归档文件的读写操作,常用于压缩和解压缩文件 |
| zipfile | 提供了对 zip 压缩文件的读写操作,用于处理 zip 文件 |
| gzip, bz2 | 用于对文件进行 gzip 和 bzip2 压缩和解压缩操作 |
| mmap | 用于内存映射文件,将文件映射到内存中进行快速数据访问 |
| csv | 用于读写 CSV (逗号分隔值) 文件,处理电子表格和数据交换的很好选择 |
| json | 用于读写 JSON (JavaScript Object Notation) 格式的数据 |
| pickle和cPickle | 用于序列化和反序列化 |
| xml | 用于XML数据处理 |
| filecmp | 用于文件的比较 |
| fnmatch | 使用模式来匹配文件路径名 |
创建文件对象open()
open()函数用于创建文件对象, 基本语法格式为: open("文件名", "打开方式")
如果只是文件名, 代表在当前目录下的文件, 文件名可以录入全路径, 比如:D:\a\b.txt
为了减少\的输入, 可以使用原始字符串: r"d:\b.txt", 示例: f = open(r"d:\b.txt", "w")
打开方式
| 模式 | 描述 |
|---|---|
| r | 读, read模式 |
| w | 写, write模式, 如果文件不存在则创建, 如果文件存在, 则重新写新内容 |
| a | 追加append模式, 如果文件不存在则创建, 如果文件存在, 则在文件末尾追加内容 |
| b | 二进制binary模式(可与其他模式组合使用) |
| + | 读, 写模式(可与其他模式组合使用) |
文本文件对象和二进制文件对象的创建:
- 如果没有增加模式b, 则默认创建的是文本文件对象, 处理的基本单元是"字符"
- 如果是二进制模式b, 则创建的是二进制文件对象, 处理的基本单元是"字节"
文本文件的写入
文本文件的写入一般分为三个步骤: 1.创建文件对象 2.写入数据 3.关闭文件对象
代码演示:
f = open(r"d:\demo.txt", "a")
f.write("hello world")
f.close()
#######################################
with open(r"d:\demo.txt", "a") as f:
f.write("hello world")
中文乱码问题
window操作系统默认的编码是GBK, Linux操作系统默认的编码是UTF-8
当我们用open()时, 调用的是操作系统打开的文件, 默认编码是GBK
with open(r"demo.txt", "w", encoding="utf-8") as f:
s = ["hello\n", "world\n", "Python\n"]
f.writelines(s)
close()关闭文件流
由于文件底层是由操作系统控制, 所以我们打开的文件对象必须显式调用close()方法关闭文件对象
当调用close()方法时, 首先会把缓冲区数据写入文件(也可以直接调用flush()方法), 再关闭文件, 释放文件对象
为了确保打开的文件对象正常关闭, 一般结合异常机制的finally或者with关键字实现无论何种情况都能关闭打开的文件对象
代码演示:
try:
f = open(r"demo.txt", "w")
f.write("Hello, Python World.")
except BaseException as e:
print(e)
finally:
f.close()
##############################
with open(r"demo.txt", "w") as f:
f.write("Hello, Python World.")
文本文件的读取
-
read([size])
从文件中读取size个字符, 并作为结果返回, 如果没有size参数, 则读取整个文件
若已经读取到文件末尾, 再次读取会返回空字符串(指针)
-
readline()
读取一行内容作为结果返回
若已经读取到文件末尾, 再次读取会返回空字符串
-
readlines()
文本文件中, 每一行作为一个字符串存入列表中, 返回该列表
with open(r"demo.txt", "r", encoding="gbk") as f:
while True:
line = f.readline()
if not line:
break
else:
print(line, end="")
练习
为文本文件每一行末尾添加行号
代码演示
with open(r"demo.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
new_lines = [line.rstrip() + "\t#" + str(index) + "\n" for index, line in zip(range(1, len(lines)+1), lines)]
with open(r"demo.txt", "w", encoding="utf-8") as f:
f.writelines(new_lines)
二进制文件的读取和写入
二进制文件的处理方式和文本文件流程一致, 首先创建文件对象, 需要指定二进制模式, 从而创建二进制文件对象
例如:
f = open("d:\demo.txt", "wb") # 可写的, 重写模式的二进制文件对象
f = open("d:\demo.txt", "ab") # 可写的, 追加模式的二进制文件对象
f = open("d:\demo.txt", "rb") # 可读的二进制文件对象
创建成功的二进制文件对象, 仍然可以使用write(), read()实现文件的读写操作
代码演示:
with open("avatar.png", "rb") as srcFile, open("demo.png", "wb") as destFile:
for line in srcFile:
destFile.write(line)
文件对象的常用属性和方法
文件对象的属性
| 属性 | 说明 |
|---|---|
| name | 返回文件名称 |
| mode | 返回文件的打开模式 |
| closed | 若文件被关闭, 则返回True |
文件对象的打开模式
| 模式 | 描述 |
|---|---|
| r | 读, read模式 |
| w | 写, write模式, 如果文件不存在则创建, 如果文件存在, 则重新写新内容 |
| a | 追加append模式, 如果文件不存在则创建, 如果文件存在, 则在文件末尾追加内容 |
| b | 二进制binary模式(可与其他模式组合使用) |
| + | 读, 写模式(可与其他模式组合使用) |
文件对象的常用方法
| 方法名 | 说明 |
|---|---|
| read([size]) | 从文件中读取size个字符, 并作为结果返回, 如果没有size参数, 则读取整个文件; 若已经读取到文件末尾, 再次读取会返回空字符串(指针) |
| readline() | 读取一行内容作为结果返回, 若已经读取到文件末尾, 再次读取会返回空字符串 |
| readlines() | 文本文件中, 每一行作为一个字符串存入列表中, 返回该列表 |
| write(str) | 将字符串str内容写入文件 |
| writelines(s) | 将字符串列表s写入文件里面, 不添加换行符 |
| seek(offset, [whence]) | 把文件指针移动到新的位置, offset表示相对于whence的多少个字节的偏移量; offset为正往结束方向移动, offset为负往开始方向移动; whence不同的值表示不同含义: 0表示从文件开头开始计算(默认值), 1表示从当前位置开始计算, 2表示从文件结尾开始计算 |
| tell() | 返回文件指针的当前位置 |
| truncate([size]) | 无论指针在什么位置, 只留下指针前size个字节的内容, 其余全部删除; 如果没有传入size, 则从当前指针位置到文件结尾的内容全部删除 |
| flush() | 把缓冲区的内容写入文件, 但不关闭文件 |
| close() | 把缓冲区的内容写入文件, 同时关闭文件, 释放文件对象的相关资源 |
使用pickle序列化
序列化: 将对象转化为"串行化"数据形式, 存储到硬盘或者网络传输到其他地方
反序列化: 跟序列化相反的过程, 将读取到的"串行化数据"转化为对象
因此, 可以使用pickle模块的函数实现序列化和反序列化
Python中, 一切皆对象, 对象本质上就是一个存储数据的内存块, 有时候我们需要将内存块的数据保存到硬盘上, 或者通过网络传输到其他的计算机上, 就需要用到对象的序列化和反序列化
对象的序列化机制广泛应用到分布式和并行系统上
pickle.dump(obj, file) # obj指的是被序列化的对象, file指的是存储文件
pickle.load(file) # 从file读取数据, 反序列化成对象
import pickle
with open("data.dat", "wb") as f:
name = "rabbit"
age = 18
score = [100, 90, 80]
resume = {"name": name, "age": age, "score": score}
pickle.dump(resume, f)
with open("data.dat", "rb") as f:
resume2 = pickle.loads(f.read())
print(resume2)
CSV文件的操作
csv是逗号分隔符文本格式, 常用于数据交换, Excel文件和数据库数据的导入和导出
与Excel文件相比, CSV文件中:
- 值没有类型, 所有值都是字符串
- 不能指定字体颜色等样式
- 不能指定单元格的宽高, 不能合并单元格
- 没有多个工作表
- 不能嵌入图像图表
Python标准库的模块csv提供了读取和写入csv格式文件的对象
csv.reader对象和csv文件读取
import csv
with open(r"a.csv") as a:
a_csv = csv.reader(a) # 创建csv对象,它是一个包含所有数据的对象
header = next(a_csv) # 获得包含标题行信息的列表对象
print(header)
for row in a_csv: # 循环打印各行内容
print(row)
csv.writer对象和csv文件写入
import csv
header = ["序号", "姓名"]
rows = [("1001", "张三"), ("1002", "李四")]
with open(r"b.csv", "w") as b:
b_csv = csv.writer(b) # 创建csv对象
b_csv.writerow(header) # 写入标题行
b_csv.writerows(rows) # 写入数据
os模块
os模块可以帮助我们直接对操作系统进行操作, 我们可以直接调用操作系统的可执行文件和命令, 直接操作文件和目录等
调用操作系统命令
os.system 可以帮助我们直接调用系统的命令
os.startfile 执行文件
文件和目录操作
os模块下常用操作文件的方法
| 方法名 | 描述 |
|---|---|
| remove(path) | 删除指定的文件 |
| rename(src, dest) | 重命名文件或目录(源目录, 目标目录) |
| stat(path) | 返回文件的所有属性 |
| listdir(path) | 返回path目录下的文件和目录列表 |
os模块目录操作的相关方法
| 方法名 | 描述 |
|---|---|
| mkdir(path) | 创建目录 |
| makedirs(path1/path2/path3) | 创建多级目录 |
| rmdir(path) | 删除目录 |
| removedirs(path1/path2/path3) | 删除多级目录 |
| getcwd() | 返回当前工作目录 |
| chdir(path) | 把path设置为当前工作目录 |
| walk() | 遍历目录树 |
| sep | 当前操作系统所使用的路径分隔符 |
repr()方法可以显示数据信息, 帮助分析数据
代码演示:
import os
print(os.name) # windows>nt linux>posix
print(os.sep) # windows>\ linux>/
print(repr(os.linesep)) # windows>\r\n linux>\n
a = "3"
print(a)
print(repr(a)) # repr可以显示数据信息
# 获取文件和文件夹的相关信息
print(os.stat("demo.txt"))
# 关于文件目录的操作
print(os.getcwd()) # 获取当前工作目录
os.chdir("d:") # 切换当前工作目录为d盘
"""创建/删除目录"""
os.mkdir("电影")
os.rmdir("电影")
"""创建多级目录"""
os.makedirs("电影/港台/周星驰")
os.rename("电影", "movie")
dirs = os.listdir("movie") # 查看子目录文件(无法查看子目录下级所有文件)
print(dirs)
os.path模块
os.path模块提供了目录相关操作(路径判断, 路径切分, 路径连接, 文件夹遍历)
| 方法 | 描述 |
|---|---|
| isabs(path) | 判断path是否为绝对路径 |
| isdir(path) | 判断path是否为目录 |
| isfile(path) | 判断path是否为文件 |
| exists(path) | 判断指定路径的文件是否存在 |
| getsize(filename) | 返回文件的大小 |
| abspath(path) | 返回绝对路径 |
| dirname(path) | 返回目录路径名称 |
| getatime(filename) | 返回文件的最后访问时间 |
| getmtime(filename) | 返回文件的最后修改时间 |
| walk(top, func, arg) | 递归方式遍历目录 |
| join(path, *paths) | 连接多个path |
| split(path) | 对路径进行分割, 以列表形式返回 |
| splitext(path) | 对路径中分割文件的扩展名 |
代码演示
import os.path
current_dir = os.getcwd()
file_list = os.listdir(current_dir)
print(file_list)
for file_name in file_list:
pos = file_name.rfind(".")
if file_name[pos + 1:] == "py":
print(pos)
print(file_name)
print("############################")
file_name = [file_name for file_name in file_list if file_name.endswith(".py")]
print(file_name)
walk()递归遍历文件和目录
os.walk()方法是一个简单易用的文件\目录遍历器, 可以帮助我们高效的处理文件\目录, 格式如下: os.walk(top[, topdown=True])
其中top表示要遍历的目录, topdown为可选, True表示先遍历top目录再遍历子目录
返回三元组(root, dirs, files):
root: 表示当前正在遍历的文件夹本身
dirs: 一个列表, 该文件夹中所有的目录名字
files: 一个列表, 该文件夹中所有的文件名字
代码演示
import os
path = os.getcwd()
list_files = os.walk(path, topdown=True)
for root, dirs, files in list_files:
for name in files:
print(os.path.join(root, name))
for name in dirs:
print(os.path.join(root, name))
shutil模块(拷贝和压缩)
shutil模块是python标准库中提供的, 主要用来做文件和文件夹的拷贝\移动\删除等; 还可以做文件和文件夹的压缩\解压操作
os模壳提供了对目录或文件的一般操作, shutil模块作为补充, 提供了移动\复制\压缩\解压等操作
代码演示
文件(夹)拷贝
import shutil
shutil.copyfile("demo.txt", "demo_copy.txt") # 文件拷贝
shutil.copytree("movie", "copy_movie") # 文件夹递归拷贝
shutil.copytree("movie", "ignore_movie", ignore=shutil.ignore_patterns("*.html", "*.htm")) # 文件夹递归拷贝(过滤模式)
文件压缩和解压
import shutil
import zipfile
# 将文件夹下的所有内容压缩到目标文件夹下生成*.zip
shutil.make_archive("zip_dir/zip_file", "zip", "movie")
# 指定多个文件压缩到一个zip文件
z = zipfile.ZipFile("demo.zip", "w")
z.write("demo.txt")
z.write("demo_copy.txt")
z.close()
# 解压文件
z2 = zipfile.ZipFile("demo.zip", "r")
z2.extractall("./ext")
z2.close()
练习: 递归算法-目录树结构的展示
import os
import os.path
def copy_files(path, level):
current_files = os.listdir(path)
for file in current_files:
file_path = os.path.join(path, file)
print("\t" * level + file_path[file_path.rfind(os.sep):])
if os.path.isdir(file_path):
copy_files(file_path, level + 1)
copy_files("movie", 0)
##########################################
# \Chinese
# # \123.html
# # \cartoon_movie
# # \demo.html
# # \demo.txt
# # \test.txt