Python语法查缺补漏 第六章:I/O编程与异常
一、OS模块基础操作
1. 查看系统平台
使用os.name
可以查看当前操作系统的名字,Windows用字符串“nt”表示,Linux用字符串“posix”表示。
2. 查看路径分隔符
不同操作系统可能会使用不同的路径分隔符。例如,Windows系统以’\‘作为路径分隔符,而Linux系统以’/'作为路径分隔符。使用os.sep
可以获取当前系统平台的路径分隔符。
3. 当前工作目录
使用os.getcwd
函数可以获取当前工作目录。
4. 获取环境变量值
os.environ
是一个包含所有环境变量值的映射对象,在Python控制台下直接输入os.environ即可查看当前所有环境变量。
如果要查看某一个环境变量值,则可以采用以下方式:os.environ[key]
或os.getenv(key)
其中,os.getenv
是一个函数,其功能是根据参数指定的键名返回对应的环境变量值。
5. 获取文件和目录列表
os.listdir(path='.')
其中,path是要获取文件和目录名字的路径,默认值’.'表示获取当前路径下的所有文件和目录的名字。返回值是由path路径下所有文件和目录名字组成的列表。
二、目录的创建及删除
1. 创建目录
os.mkdir(path)
os.makedirs(path)
os.mkdir
函数只能用于创建路径中的最后一个目录,即要求路径中除最后一个目录外前面的目录应该都存在;而os.makedirs
函数能够用于依次创建路径中所有不存在的目录。
如果要创建的目录已经存在,则os.mkdir和os.makedirs函数都会给出FileExistsError错误,即“当目录已存在时,无法创建该目录”。
2. 删除目录
使用os.rmdir(path)
可以删除指定路径的最后一层目录。这个函数只能用于删除空目录(即
目录中不包含子目录和文件)。如果要删除的目录不为空,则系统会给出OSError错误。
使用os.removedirs(path)
可以删除指定路径的最后多层目录。与os.rmdir
函数相同,os.removedirs
函数只能删除空目录。os.removedirs
函数会从指定路径中的最后一个目录开始逐层向前删除,直到指定路径中的所有目录都删除完毕或遇到一个不为空的目录。
三、相对路径和绝对路径
1. 相对路径
相对路径是指相对于当前工作目录指定的路径,其中“.”表示当前目录,而“…”表示上一层目录。
2. 绝对路径
绝对路径是指从最顶层目录开始所给出的完整的路径。
使用os.path.abspath(path)
函数可以获取指定相对路径的绝对路径,其中path是文件的相对路径。
注意:编写程序时应尽量使用相对路径,这样当把编写好的程序从一台机器复制到另一台机器上时也可以正常运行;而如果使用绝对路径,则通常需要根据另一台机器的目录结构对程序中使用的所有绝对路径做修改,造成了工作量的增加。
3. 获取文件所在目录的路径及文件名
os.path.dirname(path)
可以可以获取文件所在目录的路径,即返回path中去除文件名后的路径。
os.path.basename(path)
可以获取指定路径中的文件,即返回path中的文件名。
os.path.split(path)
可以将指定路径分解成路径名和目录/文件名两部分,其作用是返回一个由path分解得到的路径名和目录/文件名组成的元组。
4. 分离文件扩展名
使用os.path.splitext(path)
函数可以将扩展名从指定路径中分离出来,其作用是将path所指定的路径分解为一个元组(root, ext),其中ext是扩展名,root是扩展名前面的内容。
5. 路径连接
使用os.path.join(path, *paths)
函数一个路径的多个组成部分用系统路径分隔符(即os.sep
)连接在一起。其作用是将各参数用系统路径分隔符连接得到的结果返回。
四、os模块中的条件判断
1. 判断指定路径目标是否为文件
os.path.isfile(path)
如果是文件,返回True;反之返回False。
2. 判断指定路径目标是否为目录
os.path.isdir(path)
如果是目录,返回True;反之返回False。
3. 判断指定路径是否存在
os.path.exists(path)
如果路径存在,返回True;反之返回False。
可以用以下程序在对文件进行操作时先确保其路径存在:
if not os.path.isdir(path):
os.makedirs(path)
4. 判断指定路径是否为绝对路径
os.path.isabs(path)
如果是绝对路径,返回True;反之返回False。
五、文件打开与关闭
1. 文件打开
使用open函数可以打开一个要做读/写操作的文件,其常用形式为:open(filename, mode='r')
。其中,filename是要打开文件的路径;mode是文件打开方式,不同文件打开方式可以组合使用,默认打开方式为’r’(等同于’rt’)。
使用open函数打开文件后会返回一个文件对象,利用该文件对象可完成文件中数据的读写操作。
文件打开方式:
文件打开方式 | 描述 |
---|---|
‘r’ | 以只读方式打开文件,不允许写数据 |
‘w’ | 以写方式打开文件,不允许读数据。文件已存在会先将文件内容清空,文件不存在会创建文件 |
‘a’ | 以追加写方式打开文件,不允许读数据。在文件已有数据后继续向文件中写新数据,文件不存在会创建文件 |
‘b’ | 以二进制方式打开文件 |
‘t’ | 以文本方式打开文件(默认) |
‘+’ | 以读写方式打开文件,可以读/写数据 |
常用文件打开方式组合:
文件打开方式 | 描述 |
---|---|
'r+‘或’rt+’ | 以文本方式打开文件并可以对文件进行读/写操作。文件不存在会报错 |
'w+‘或’wt+’ | 以文本方式打开文件并可以对文件进行读/写操作。文件不存在会新建文件,文件已存在会清空文件内容 |
'a+‘或’at+’ | 以文本、追加写方式打开文件,可对文件进行读/写操作。文件不存在会创建文件,文件已存在则文件指针会自动移动到文件尾 |
‘rb+’ | 与’r+'类似,只是以二进制方式打开文件 |
‘wb+’ | 与’w+'类似,只是以二进制方式打开文件 |
‘ab+’ | 与’a+'类似,只是以二进制方式打开文件 |
注意:文件中有一个文件指针,其指向当前要读/写数据的位置。在打开文件时,如果打开方式中不包括’a’,则文件指针指向文件首的位置;随着读/写操作,文件指针顺序向后移动,直至读写完毕。如果打开方式中包括’a’,则文件指针指向文件尾的位置,此时向文件中写数据时就会在已有数据后写入新数据。
2. 文件关闭
使用open函数打开文件并完成读/写操作后,必须使用文件对象的close方法将文件关闭。
例如,假设有一个文件对象f,则在对f所对应的文件完成读/写操作后,应使用f.close()
关闭文件。
f = open('D:\\Python\\test.txt', 'w+')
print('文件已关闭:',f.closed)
f.close()
print('文件已关闭:',f.closed)
输出为:
文件已关闭:False
文件已关闭:True
3. with语句
使用with语句可以让系统在文件操作完毕后自动关闭文件,从而避免忘记调用close方法而不能及时释放文件资源的问题。
with open('D:\\Python\\test.txt','w+') as f:
pass
print('文件已关闭:',f.closed)
输出为:
文件已关闭:True
六、文件对象方法
1. write方法
使用f.write(str)
方法可以将字符串写入到文件中。其中,f是open函数返回的文件对象,str是要写入到文件中的字符串。f.write函数执行完毕后将返回写入到文件中的字符数。
charnum=0
with open('D:\\Python\\test.txt','w+') as f:
charnum += f.write('Python是一门流行的编程语言!\n')
charnum += f.write('我喜欢学习Python语言!')
print('总共向文件中写入的字符数:%d'%charnum)
输出为:
总共向文件中写入的字符数:32
需要注意的是使用write方法向文件中写入一个字符串后并不会自动在字符串后加换行。如果加换行的话,需要人为向文件中写入换行符’\n ’ 。write方法返回的写入文件的字符数包括换行符’\n’。
2. read方法
使用f.read(n=-1)
可以从文件中读取数据。其中,f是open函数返回的文件对象;n指定了要读取的字节数,默认值-1表示读取文件中的所有数据。read方法将从文件中读取的数据返回。
with open('D:\\Python\\test.txt','r') as f:
content1 = f.read()
content2 = f.read()
print('content1:\n%s'%content1)
print('content2:\n%s'%content2)
输出为:
content1:
Python是一门流行的编程语言!
我喜欢学习Python语言!
content2:
其中第一次调用read方法时一次性地把文件中的所有数据读取到了content1中,且此时文件指针自动移动到刚读取数据的后面(即文件尾);第二次再调用read方法时不会读取到任何数据,因此content2是一个空字符串。
3. readline方法
使用f.readline()
可以从文件中每次读取一行数据。其中,f是open函数返回的文件对象。readline方法将从文件中读取的一行数据返回。
ls=[]
with open('D:\\Python\\test.txt','r') as f:
ls.append(f.readline())
ls.append(f.readline())
print(ls)
输出为:
[‘Python是一门流行的编程语言!\n’, ‘我喜欢学习Python语言!’]
4. readlines方法
使用f.readlines()
方法可以从文件中按行读取所有数据。其中,f是open函数返回的文件对象。readlines方法将从文件中按行读取的所有数据以列表形式返回。
with open('D:\\Python\\test.txt','r') as f:
ls1 = f.readlines() # readlines方法和list函数会得到同样的结果
ls2 = list(f)
print(ls1)
print(ls2)
输出为:
[‘Python是一门流行的编程语言!\n’, ‘我喜欢学习Python语言!’]
[‘Python是一门流行的编程语言!\n’, ‘我喜欢学习Python语言!’]
5. seek方法
使用f.seek(pos, whence=0)
可以移动文件指针,从而实现文件的随机读写。其中,f是open函数返回的文件对象;pos是要移动的字节数;whence是参照位置,默认值0表示以文件首作为参照位置,1和2分别表示以当前文件指针位置和文件尾作为参照位置。seek方法没有返回值。
with open('D:\\Python\\test.txt','r') as f:
f.seek(6, 0)
print(f.readline())
输出为:
是一门流行的编程语言!
注意:当以文本方式打开文件后,只支持以文件首作为参照位置进行文件指针的移动;而以二进制方式打开文件后,可以支持全部的三种参照位置。通过seek方法实现的文件随机读写主要用于二进制文件,建议尽量不对文本文件进行随机读写。与seek对应的还有一个tell方法,其用于获取当前文件指针的位置。
七、csv操作一维、二维数据
1. 一维数据
一维数据是指数据元素的值由一个因素唯一确定。对于一维有序数据,可以使用列表存储;对于一维无序数据,可以使用集合存储。
2. 二维数据
二维数据是指数据元素的值由两个因素共同确定。通过二维列表可以存储二维数据。
3. csv
CSV(Comma-Separated Values)是一种国际通用的一维、二维数据存储格式,其对应文件的扩展名为.csv,可使用Excel软件直接打开。
CSV文件中每行对应一个一维数据,一维数据的各数据元素之间用英文半角逗号分隔(逗号两边不需要加额外的空格);对于缺失元素,也要保留逗号,使得元素的位置能够与实际数据对应。
CSV文件中的多行形成了一个二维数据,即一个二维数据由多个一维数据组成;二维数据中的第一行可以是列标题,也可以直接存储数据(即没有列标题)。
4. csv文件的写操作
csv.writer(csvfile)
可以生成一个writer对象,使用该对象可以将数据以逗号分隔的形式写入到CSV文件中。其中,csvfile是一个具有write方法的对象。如果将open函数返回的文件对象作为实参传给csvfile,则调用open函数打开文件时必须加上一个关键字参数“newline=’’”。
生成writer对象后,就可以使用csv模块的writer.writerow(row)
和writer.writerows(rows)
方法向CSV文件中写入数据。其中,writer是csv.writer方法返回的writer对象;row是要写入到CSV文件中的一行数据(如一维列表);rows是要写入到CSV文件中的多行数据(如二维列表)。
4. csv文件的读操作
csv.reader(csvfile)
可以生成一个reader对象,使用该对象可以将以逗号分隔的数据从CSV文件读取出来。其中,csvfile要求传入一个迭代器。open函数返回的文件对象除了是可迭代对象,同时也是迭代器。如果将文件对象作为实参传给csvfile,则调用open函数打开文件时应加上一个关键字参数“newline=’’”。
返回的reader对象是一个可迭代对象,因此可以使用for循环直接遍历CSV文件中的每一行数据,每次遍历会返回一个由字符串组成的列表。
import csv # 导入csv模块
data2D = [[90, 98, 87], # 第1名学生的3门课程成绩
[70, 89, 92], # 第2名学生的3门课程成绩
[95, 78, 81], # 第3名学生的3门课程成绩
[98, 90, 95], # 第4名学生的3门课程成绩
[65, 72, 70]] # 第5名学生的3门课程成绩
with open('C:/Users/wangzian/Desktop/score.csv', 'w', newline='') as f: # 打开文件
csvwriter = csv.writer(f) # 得到writer对象
csvwriter.writerow(['语文', '数学', '英语']) # 先将列标题写入CSV文件
csvwriter.writerows(data2D) # 将二维列表中的数据写入CSV文件
ls2 = []
with open('C:/Users/wangzian/Desktop/score.csv', 'r', newline='') as f: # 打开文件
csvreader = csv.reader(f) # 得到reader对象
for line in csvreader: # 将CSV文件中的一行数据作为列表读取到line中
ls2.append(line) # 将当前行数据的列表添加到ls2的尾部
print(ls2) # 输出ls2
输出为:
[[‘语文’, ‘数学’, ‘英语’], [‘90’, ‘98’, ‘87’], [‘70’, ‘89’, ‘92’], [‘95’, ‘78’, ‘81’], [‘98’, ‘90’, ‘95’], [‘65’, ‘72’, ‘70’]]
八、异常
1. 异常的定义
异常是指因程序运行时发生错误而产生的信号。
如果程序中没有对异常进行处理,则程序会抛出该异常并停止程序运行。为了保证程序的稳定性和容错性,我们需要在程序中捕获可能的异常并对其进行处理,使得程序不会因异常而意外停止。
2. 异常的分类
(1) 语法错误
语法错误是指编写的程序不符合编程语言的语法要求。
(2) 逻辑错误
逻辑错误是指虽然编写的程序符合编程语言的语法要求,但要执行的数据操作不被系统或当前环境所支持。
异常 | 描述 |
---|---|
AssertionError | 当assert语句失败时引发该异常 |
AttributeError | 当访问一个属性失败时引发该异常 |
ImportError | 当导入一个模块失败时引发该异常 |
IndexError | 当访问序列数据的下标越界时引发该异常 |
KeyError | 当访问一个映射对象(如字典)中不存在的键时引发该异常 |
MemoryError | 当一个操作使内存耗尽时引发该异常 |
NameError | 当引用一个不存在的标识符时引发该异常 |
OverflowError | 当算术运算结果超出表示范围时引发该异常 |
RecursionError | 当超过最大递归深度时引发该异常 |
RuntimeError | 当产生其他所有类别以外的错误时引发该异常 |
StopIteration | 当迭代器中没有下一个可获取的元素时引发该异常 |
TabError | 当使用不一致的缩进方式时引发该异常 |
TypeError | 当传给操作或函数的对象类型不符合要求时引发该异常 |
UnboundLocalError | 引用未赋值的局部变量时引发该异常 |
ValueError | 当内置操作或函数接收到的参数具有正确类型但不正确值时引发该异常 |
ZeroDivisionError | 当除法或求模运算的第2个操作数为0时引发该异常 |
FileNotFoundError | 当要访问的文件或目录不存在时引发该异常 |
FileExistsError | 当要创建的文件或目录已存在时引发该异常 |
3. 异常处理
(1) try except
使用try except语句可以捕获异常并做异常处理,其语法格式为:
try:
try子句的语句块
except 异常类型1:
异常类型1的处理语句块
except 异常类型2:
异常类型2的处理语句块
…
except 异常类型N:
异常类型N的处理语句块
try except语句的处理过程为:
- 执行try子句的语句块。如果没有异常发生,则except子句不被执行。
- 如果有异常发生,则根据异常类型匹配每一个except关键字后面的异常名,并执行匹配的那个except子句的语句块;
- 如果异常类型与所有except子句都不匹配,则该异常会传给更外层的try except语句;
- 如果异常无法被任何的except子句处理,则程序抛出异常并停止运行。
for i in range(3): #循环3次
try:
num = int(input('请输入一个数字:'))
print(10 / num)
except ValueError:
print('值错误!')
except:
print('其他异常!')
输出为:
请输入一个数字:abc
值错误!
请输入一个数字:0
其他异常!
请输入一个数字:10
1.0
注意:except子句后面的异常类型,既可以是单个异常类型,如“except ValueError:”;也可以是由多个异常类型组成的元组,如“except(TypeError, ZeroDivisionError):”;还可以为空,即“except:”,表示捕获所有的异常。
(2) else
else子句是try except语句中的一个可选项。如果try子句执行时没有发生异常,则在try子句执行结束后会执行else子句;否则,如果发生异常,则else子句不会执行。
for i in range(2): #循环2次
try:
num = int(input('请输入一个数字:'))
print(10 / num)
except ValueError:
print('值错误!')
else:
print('else子句被执行!')
输出为:
请输入一个数字:abc
值错误!
请输入一个数字:10
1.0
else子句被执行!
(3) finally
finally子句是try except语句中的另一个可选项。无论try子句执行时是否发生异常,finally子句都会被执行。
for i in range(2): #循环2次
try:
num = int(input('请输入一个数字:'))
print(10 / num)
except ValueError:
print('值错误!')
finally:
print('finally子句被执行!')
输出为:
请输入一个数字:abc
值错误!
finally子句被执行!
请输入一个数字:10
1.0
finally子句被执行!
(3) raise
除了系统遇到错误产生异常外,我们也可以使用raise产生异常。
for i in range(2): #循环2次
try:
num=int(input('请输入一个数字:'))
if num==0:
raise ValueError('输入数字不能为0!')
print(10/num)
except ValueError as e:
print('值错误:',e)
输出为:
请输入一个数字:0
值错误: 输入数字不能为0!
请输入一个数字:10
1.0
(4) 断言assert
使用assert可以判断一个条件是否成立,如果成立则继续执行后面的语句;如果不成立则会引发AssertionError异常。
for i in range(2): #循环2次
try:
num = int(input('请输入一个数字:'))
assert num != 0
print(10 / num)
except AssertionError:
print('断言失败!输入为0!')
输出为:
请输入一个数字:0
断言失败!输入为0!
请输入一个数字:10
1.0
(5) 自定义异常
自定义异常,实际上就是以BaseException类作为父类创建一个子类。
class ScoreError(BaseException): #以BaseException类作为父类创建ScoreError类
def __init__(self,msg): #定义构造方法
self.msg = msg # message
def __str__(self): #定义__str__方法,将ScoreError类对象转换为字符串时自动调用
return self.msg
if __name__=='__main__':
for i in range(2): #循环2次
try:
score=int(input('请输入一个成绩:'))
if score<0 or score>100:
raise ScoreError('输入成绩为%d,成绩应在0-100之间'%score)
print('输入成绩为%d'%score)
except ScoreError as e:
print('分数错误:',e)
输出为:
请输入一个成绩:90
输入成绩为90
请输入一个成绩:-1
分数错误: 输入成绩为-1,成绩应在0-100之间