文章导读
本文主要用于记录python中的异常机制,文件处理和模块的知识点及其练习,学习本文前需要有一定的python基础。
1、异常机制
1.1、初识异常
异常机制的概述:当程序出现异常,程序安全的退出、处理完后继续执行的机制。
python中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。下面较为完整的展示了python中内建异常类的继承层次:
提示:Python中一切皆对象,异常也采用对象的方式来处理。
处理异常的过程:①、抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给解释器。
②、捕获异常:解释器得到该异常后,寻找相应的代码来处理该异常。
1.2、try...except...结构
概述:try_except结构是常见的异常处理结构。
语法:
try:
被监控的可能引发异常的语句块
except BaseException [as e]:
异常处理语句块
实操1:首先测试被监控的可能引发异常的语句块没有产生异常的情况。
try:
print("step1")
a = 3/4
print("step2")
except BaseException as e:
print("step3")
print(e)
print("step4")
运行结果如下:
接下来测试被监控的可能引发异常的语句块产生异常的情况(这里的异常是除数为0)。
try:
print("step1")
a = 3/0
print("step2")
except BaseException as e:
print("step3")
print(e)
print("step4")
运行结果如下:
实操2:通过try_except结构监测用户输入的是否为一个数字,如果不是数字就抛出异常体系和具体的报错信息。
try:
getNum = int(input("请输入任意一个数字:"))
print("输入的数字为{}".format(getNum))
# 如果不需要给出报错信息可以只写except:
except BaseException as e:
print("~输入的内容不是数字~")
print("具体的报错信息为:{}".format(e))
运行结果如下:
1.3、try...多个except...结构
概述:上面的结构可以捕获所有的异常,工作中也很常见。但是,从经典理论考虑,一般建议尽量捕获可能出现的多个异常(按照先子类后父类的顺序),并且针对性的写出异常处理代码。为了避免遗漏可能出现的异常,可以在最后增加BaseException。
语法:
try:
被监控的、可能引发异常的语句块
except Exception1:
处理Exception1的语句块
except Exception2:
处理Exception2的语句块
[...]
except BaseException:
处理可能遗漏的异常的语句块
实操:通过try_多个except监视获取两个数,并将两个数进行相除,如有错就打印报错信息。
try:
a = input("请输入被除数:")
b = input("请输入除数:")
c = float(a)/float(b)
print(c)
# ZeroDivisionError表示当除数为0时就会抛出异常
except ZeroDivisionError:
print("除数不能为0")
except TypeError:
print("TypeError:类型不匹配")
# 由于BaseException是所有异常类的父类,所以写在最后
except BaseException as e:
print("没有具体指定的报错:{}".format(e))
print(type(e))
运行结果如下:
1.4、try...except...else...结构和return的位置
概述:在try...except...结构的基础上增加else...表示如果try...except...结构中没有产生异常就会执行else语句块中的内容,否则就不执行else语句块的内容。
实操:通过案例熟悉try...except...else...结构。
try:
num1 = input("请输入被除数:")
num2 = input("请输入除数:")
num3 = float(num1)/float(num2)
except BaseException as e:
print("程序报错为:{}".format(e))
else:
print("{}/{}={}".format(num1, num2, num3))
print("程序无异常!")
运行结果如下:
1.5、try...except...finally...结构
概述:finally块无论是否发生异常都会被执行,通常用来释放try块中申请的资源。
实操1:通过案例了解finally块的作用。
try:
num1 = input("请输入被除数:")
num2 = input("请输入除数:")
num3 = float(num1)/float(num2)
except BaseException as e:
print("程序报错为:{}".format(e))
else:
print("{}/{}={}".format(num1, num2, num3))
print("程序无异常!")
finally:
print("无论是否发生异常,finally语句都会执行")
print("程序结束...")
运行结果如下:
实操2:模拟打开文件并读取文件第一行的内容并打印,如果没有异常还需关闭文件,无论是否有错误都需提示try...except...finally...块执行结束。
try:
# 打开D盘的1.txt并进行读操作
f = open("D:/1.txt","r")
# 对f的第一行进行读操作
content = f.readline()
# 打印读取到的第一行的信息
print(content)
except BaseException as e:
print("报错信息:{}".format(e))
# else块的内容可省略,这里是巩固记忆
else:
f.close()
print("文件已关闭")
finally:
print("try...except...finally...结构执行完成")
print("程序结束...")
运行结果如下:
return位置的概述:由于return有两种作用:结束方法运行、返回值。我们一般不把return放到异常处理结构中,而是放到方法最后。
实操3:通过案例了解return语句在异常结构中的位置。
def fun():
try:
num = 20/4
except BaseException as e:
print("错误信息为:{}".format(e))
return num
print(fun())
运行结果如下:
1.6、常见异常汇总
概述:Python中的异常都派生自BaseException类。
# SyntaxError:语法错误
int a =3
# 报错信息:SyntaxError: invalid syntax
# NameError:尝试访问一个没有申明的变量
print(a)
# 报错信息:NameError: name 'a' is not defined
# ZeroDivisionError:除数为0错误(零除错误)
a = 3/0
# 报错信息:ZeroDivisionError: division by zero
# ValueError:数值错误
float("gaoqi")
# 报错信息:ValueError: could not convert string to float: 'gaoqi'
# TypeError:类型错误
123+"abc"
# 报错信息:TypeError: unsupported operand type(s) for +: 'int' and 'str'
# AttributeError:访问对象的不存在的属性
a=100
a.sayhi()
# 报错信息:AttributeError: 'int' object has no attribute 'sayhi'
# IndexError:索引越界异常
a = [4,5,6]
a[10]
# 报错信息:IndexError: list index out of range
# KeyError:字典的关键字不存在
a = {'name':"gaoqi",'age':18}
a['salary']
# 报错信息:KeyError: 'salary'
异常名称 | 说明 |
---|---|
ArithmeticError | 所有数值计算错误的基类 |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
BaseException | 所有异常的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
EnvironmentError | 操作系统错误的基类 |
EOFError | 没有内建输入,到达EOF 标记 |
Exception | 常规错误的基类 |
FloatingPointError | 浮点计算错误 |
FutureWarning | 关于构造将来语义会有改变的警告 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
ImportError | 导入模块/对象失败 |
IndentationError | 缩进错误 |
IndexError | 序列中没有此索引(index) |
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 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeError | Unicode 相关的错误 |
UnicodeTranslateError | Unicode 转换时错误 |
UserWarning | 用户代码生成的警告 |
ValueError | 传入无效的参数 |
Warning | 警告的基类 |
WindowsError | 系统调用失败 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
1.7、with上下文的管理
概述:finally块由于是否发生异常都会执行,通常我们放释放资源的代码。其实,我们可以通过with上下文管理,更方便的实现释放资源的操作。
语法:
# context_expr表示打开资源的代码
with context_expr [ as var]:
语句块
补充:with上下文管理可以自动管理资源,在with代码块执行完毕后自动还原进入该代码之前的现场或上下文。不论何种原因跳出with块,不论是否有异常,总能保证资源正常释放。极大的简化了工作,在文件操作、网络通信相关的场合非常常用。
实操:了解with的使用。(实操前需要在D盘创建1.txt文件,并在文件中写入任意内容并保存。)
with open("d:/1.txt") as f:
# 读取一行的信息
print("读取一行的信息:{}".format(f.readline()))
# 读取多行信息
print("读取多行信息:")
for line in f:
print(line)
运行结果如下:
1.8、traceback模块和异常写入日志
概述:traceback模块是 Python 中用于提取和格式化异常回溯信息的工具。当 Python 程序出现异常时,traceback模块可以用来获取异常发生时的调用栈信息,这对于诊断和调试代码非常有用。调用栈信息包括了程序执行过程中所有的函数调用以及它们的位置。
实操:通过traceback将异常信息写入D盘中1.log文件中(open方法的参数a可以在文件不存在时自动创建文件并执行对应的操作,所以无需提前创建文件)
# 导入traceback
import traceback
try:
num1 = float(input("输入被乘数:"))
num2 = float(input("输入乘数:"))
num3 = num1/num2
except:
# open括号中的a表示append
with open("d:/1.log","a") as f:
traceback.print_exc(file=f)
运行结果如下:
1.log文件内容如下:
1.9、自定义异常类
概述:程序开发中,有时候我们也需要自己定义异常类。自定义异常类一般都是运行时异常,通常继承Exception或其子类即可。命名一般以Error,Exception为后缀。自定义异常由raise语句主动抛出。
实操:通过如下案例熟悉自定义异常类。
# 自定义异常类(一定要继承Exception或其子类)
class AgeError(Exception):
# 构造方法
def __init__(self,errorInfo):
Exception.__init__(self)
self.errorInfo = errorInfo
# 实例方法
def __str__(self):
return str(self.errorInfo)+"超过了18~60的范围"
if __name__ == "__main__":
age = int(input("请输入您的年龄:"))
if age<18 or age>60:
# 自定义异常类需要通过raise抛出
raise AgeError(age)
else:
print("您输入的年龄符合!")
运行结果如下(当输入的年龄超过了我们的限制范围):
提醒:自定义异常类一般都是运行时异常,通常继承Exception
或其子类即可。命名一般以Error,Exception为后缀。
2、文件处理
2.1、初识文件
文件概述:按文件中数据组织形式,我们把文件分为文本文件和二进制文件两大类。
文本文件:文本文件存储的是普通“字符”文本,python默认为unicode字符集(两个字节表示一个字符,最多可以表示:65536个),可以使用记事本程序打开。
二进制文件:二进制文件把数据内容用“字节”进行存储,无法用记事本打开。必须使用专用的软件解码。常见的有:MP4视频文件、MP3音频文件、JPG图片、doc文档等等。
Python标准库中,如下是文件操作相关的模块:
名称 | 说明 |
---|---|
io模块 | 文件流的输入和输出操作 input output |
os模块 | 基本操作系统功能,包括文件操作 |
glob模块 | 查找符合特定规则的文件路径名 |
fnmatch模块 | 使用模式来匹配文件路径名 |
fileinput模块 | 处理多个输入文件 |
filecmp模块 | 用于文件的比较 |
csv模块 | 用于csv文件处理 |
pickle和cPickle | 用于序列化和反序列化 |
xml包 | 用于XML数据处理 |
bz2、gzip、zipfile、zlib、tarfile | 用于处理压缩和解压缩文件(分别对应不同的算法) |
2.1.1、创建文件对象open()
概述:open()函数用于创建文件对象。
语法:
open(文件名[,打开方式])
知识补充:①、如果只是文件名,代表在当前目录下的文件;文件名可以录入全路径,比如:D:\a\1.txt
②、由于\与其他字母组合后有特殊含义(转义字符),所以书写时会在\前加上一个\,让程序知道这里不是转义字符,使得需要写很多\,为了减少\
的输入,可以使用原始字符串:r":d:\b.txt"。
打开文件的方式:
模式 | 描述 |
---|---|
r | 读 read模式 |
w | 写 write模式。如果文件不存在则创建;如果文件存在,则重写新内容; |
a | 追加append模式。如果文件不存在则创建;如果文件存在,则在文件末尾追加内容 |
b | 二进制binary模式(可与其他模式组合使用) |
+ | 读、写模式(可与其他模式组合使用) |
补充:①、如果没有增加模式b,则默认创建的是文本文件对象,处理的基本单元是“字符”。
②、如果是二进制模式b,则创建的是二进制文件对象,处理的基本单元是“字节”。
文本文件写入的步骤:①、创建文件对象;②、写入数据;③、关闭文件对象。
注意:写入数据后一定要关闭文件对象,否则操作系统会一直等待程序的指令去操作文件。
实操1:进行文本操作测试,了解文本操作的基本流程。
# w表示重写写入(无论文件之前有多少内容都会被清空)
# 注意:w和a两种模式在文件不存在时会创建新的文件并操作
f = open(r"d:1.txt","w")
f.write("hello open()")
f.close()
D盘下1.txt文件内容为:
实操2:结合with对文本进行操作。
with open(r"d:1.txt","w") as f:
f.write("通过with对文本文件进行了覆写")
# 由于with会自动关闭文件,所以不用f.close()
D盘下1.txt文件内容为:
知识点补充:①、write(a):表示将字符串a写入到文件中。
②、writelines(b):将字符串列表写入到文件中,不自带换行符,如需换行可通过\n实现。
实操3:通过实例了解writelines()的用法。
f = open(r"d:1.txt","w",encoding="utf-8")
b = ["keep","study\n","habits"]
f.writelines(b)
f.close()
D盘下1.txt文件内容为:
2.2、关闭流
2.2.1、finally异常管理
概述:①、由于文件底层是由操作系统控制,所以我们打开的文件对象必须显式调close()方法关闭文件对象。当调用close()方法时,首先会把缓冲区数据写入文件(也可以直接调flush()方法),再关闭文件,释放文件对象。
②、为了确保打开的文件对象正常关闭,一般结合异常机制的finally或者with关键字实现无论何种情况都能关闭打开的文件对象。
实操:结合异常机制里的with,确保关闭了文件对象。
try:
f = open(r"d:1.txt","w",encoding="utf-8")
s = ["new String info"]
f.writelines(s)
except BaseException as e:
print("error:{}".format(e))
finally:
f.close()
运行后的d:1.txt文件内容如下:
2.2.2、with上下文管理
概述:with(上下文管理器)可以自动管理上下文资源,不论什么原因跳出with块,都能确保文件正确的关闭,并且可以在代码块执行完毕后自动还原进入该代码块时的现场。
实操:巩固with上下文管理器对文本文件的写入。
s = ["keep\n","study\n","habits"]
with open(r"d:3.txt","a",encoding="utf-8") as f:
f.writelines(s)
运行后的d:3.txt文件内容如下:
2.3、文本文件的读取
文本文件读取的三种方式:
①、read([size])
概述:从文件中读取size个字符,并作为返回结果,如果没有指定size参数,将读取整个文件。如果已经读取到文件末尾,再次对该文件进行读取时会返回一个空字符串。
②、readline()
概述:读取一行内容作为结果返回。读取到文件末尾,会返回空字符串。
③、readlines()
概述:文本文件中,每一行作为一个字符串存入列表中,返回该列表。
实操1:利用read([size])对文本内容进行读取。(首先为D盘下的文件1.txt写入如下内容并保存。)
北京
上海
广州
深圳
成都
武汉
长沙
苏州
with open(r"d:1.txt","r",encoding="utf-8") as f:
getInfo = f.read(5)
print("读取到的5个字符:\n{}".format(getInfo))
getInfo_agen = f.read(2)
print("再次读取2个字符的内容为:\n{}".format(getInfo_agen))
getInfo_thr = f.read()
print("最后读取所有的内容为:\n{}".format(getInfo_thr))
运行结果如下:
结果分析:不难发现,在读取文件时,换行符( \n )也会被当作一个字符。
实操2:通过readline()方法对文本内容进行读取。
with open(r"d:1.txt","r",encoding="utf-8") as f:
f1 = f.readline()
f2 = f.readline()
f3 = f.readline()
print("第一行的内容为:{}第二行的内容为:{}第三行的内容为:{}".format(f1,f2,f3))
运行结果如下:
实操3:通过readlines()获取文件中所有行的内容。
with open(r"d:1.txt","r",encoding="utf-8") as f:
f1 = f.readlines()
print("通过readlines获取的内容为:{}".format(f1))
运行结果如下:
结果分析:通过实操不难发现,通过readlines()获取的内容会以行为单位,每一行就是返回列表的一个元素,并且换行符也会被当作每一行的内容。
实操4:通过for循环打印文本文件中的内容。
with open(r"d:1.txt","r",encoding="utf-8") as f:
for line in f:
print(line,end="")
运行结果如下:
实操5:结合while循环,if...else...选择分支和readline()方法获取文件中的内容。
with open(r"d:1.txt","r",encoding="utf-8") as f:
while True:
line_info = f.readline()
if not line_info:
break
else:
print(line_info,end="")
运行结果如下:
2.4、文本操作练习之为文本文件添加行号
实操:为文本末尾添加行号提示。
解题思路:首先通过readlines()方法将文件获取并保存,然后利用列表推导式对获取的列表进行修改,修改完成后再将修改好的内容写入到文件中即可。
# 读取文件内容并做指定修改
with open(r"d:1.txt","r",encoding="utf-8") as f:
lines = f.readlines()
# rstrip()表示去除右侧空白符
lines2 =[ lines.rstrip()+" 第"+str(index)+"行\n" for index,lines in zip(range(1,(len(lines)+1)),lines)]
# 查看修改后的内容
print(lines2)
# 将修改的内容添加到文件中
with open(r"d:1.txt","w",encoding="utf-8") as f:
f.writelines(lines2)
运行后的d:1.txt的内容如下:
2.5、二进制文件的读写
概述:①、二进制文件的处理流程和文本文件流程一致。首先还是要创建文件对象,不过,我们需要指定二进制模式,从而创建出二进制文件对象。
②、创建好二进制文件对象后,仍然可以使用write()方法和read()方法实现文件的读写操作。
实操:对d盘中的图片1.jpg拷贝到d盘的test_file目录中。
with open(r"d:1.jpg","rb") as srcFile,open(r"D:\test_file\2.jpg","wb") as deskFile:
for line in srcFile:
deskFile.write(line)
运行后的D:\test_file目录中的文件如下:
提醒:进行二进制文件操作时,一定不能省略参数b,否则会被当做文本文件进行操作。
2.6、文件对象的常用属性和方法汇总
文件对象的属性:
属性 | 说明 |
---|---|
name | 返回文件的名字 |
mode | 返回文件的打开模式 |
closed | 若文件被关闭, 则返回True |
文件对象的打开模式:
模式 | 说明 |
---|---|
r | 读模式 |
w | 写模式 |
a | 追加模式 |
b | 二进制模式(可与其他模式组合) |
+ | 读写模式(可以其他模式组合) |
文件对象的常用方法:
方法名 | 说明 |
---|---|
read([size]) | 从文件中读取size个字节或字符的内容返回。若省略[size],则读取到文件末尾,即一次读取文件所有内容 |
readline() | 从文本文件中读取一行内容 |
readlines() | 把文本文件中每一行都作为独立的字符串对象,并将这些对象放入列表返回 |
write(str) | 将字符串str内容写入文件 |
writelines(s) | 将字符串列表s写入文件文件,不添加换行符 |
seek(offset [,whence]) | 把文件指针移动到新的位置,offset表示相对于whence的多少个字节的偏移量;offset:off为正往结束方向移动,为负往开始方向移动whence不同的值代表不同含义:0: 从文件头开始计算(默认值)1:从当前位置开始计算2:从文件尾开始计算 |
tell() | 返回文件指针的当前位置 |
truncate([size]) | 不论指针在什么位置,只留下指针前size个字节的内容,其余全部删除;如果没有传入size,则当指针当前位置到文件末尾内容全部删除 |
flush() | 把缓冲区的内容写入文件,但不关闭文件 |
close() | 把缓冲区内容写入文件,同时关闭文件,释放文件对象相关资源 |
实操:通过案例了解文件对象的属性和方法。
with open("d:1.txt","r",encoding="utf-8") as f:
print("文件名是:{0}".format(f.name))
print("指针当前位置为:{}".format(f.tell()))
print("读取的内容:{0}".format(str(f.readline())))
print("指针当前位置为:{}".format(f.tell()))
# 从文件开头开始算,指针移动到3
f.seek(6,0)
print("调用seek(6,0)后读取的内容:{0}".format(str(f.readline())))
运行结果如下:
2.7、使用pickle实现序列化和反序列化(了解)
概述:①、序列化指的是将对象转化成“串行化”数据形式,存储到硬盘或通过网络传输到其他地方。反序列化是指相反的过程,将读取到的“串行化数据”转化成对象。
②、Python中使用pickle模块中的函数,实现序列化和反序列操作。
补充:Python中,一切皆对象,对象本质上就是一个“存储数据的内存块”。有时候,我们需要将“内存块的数据”保存到硬盘上,或者通过网络传输到其他的计算机上。这时候,就需要“对象的序列化和反序列化”。 对象的序列化机制广泛的应用在分布式、并行系统上。
用法:
pickle.dump(obj, file) obj就是要被序列化的对象,file指的是存储的文件
pickle.load(file) 从file读取数据,反序列化成对象
实操:通过例子理解序列化和反序列化。
# 导入pickle
import pickle
# 进行序列化操作
with open("data.dat","wb") as f:
name = "沐曦是也"
age = 1024
score = [70,80,90]
resume = {"name":name,"age":age,"score":score}
# 参数1是序列化的内容,参数2是指定存储的文件
pickle.dump(resume,f)
# 进行反序列化操作
with open("data.dat","rb") as f:
resume_get = pickle.load(f)
print("反序列化得到的内容为:{}".format(resume_get))
运行后发现python文件的同级目录中新建了一个名为data.dat的文件,文件内容如下:
运行结果如下:
2.8、CSV文件的读取和写入(了解)
csv文件:
概述:csv是逗号分隔符文本格式,常用于数据交换、Excel文件和数据库数据的导入和导出。
CSV文件与Excel文件的不同点:
①、CSV文件的值没有类型,所有值都是字符串;
②、CSV文件不能指定字体颜色等样式;
③、CSV文件不能指定单元格的宽高,不能合并单元格;
④、CSV文件没有多个工作表;
⑤、CSV文件不能嵌入图像图表;
实操前首先在D盘新建一个excel文件,取名为1.xlsx,并写入任意内容,然后另存为CSV文件并保存在D盘取名为1.csv。
姓名 | 年龄 | 身高 | 性别 |
张三 | 25 | 168 | 女 |
李四 | 28 | 176 | 男 |
王五 | 26 | 174 | 男 |
实操1:了解CSV文件的读取。
import csv
# 这里可能需要调整编码,选择gbk或utf-8
with open("d:1.csv",encoding="utf-8") as a:
# 创建csv对象,通过csv.reader()获取的数据是一个包含文件中所有数据的列表,每一行作为一个元素
a_csv = csv.reader(a)
# 获得列表对象,包含标题行的信息
headers = next(a_csv)
print("表头信息:{}".format(headers))
for row in a_csv:
print(row)
运行结果如下:
实操2:了解csv文件的写入。
import csv
# 准备写入的数据
headers = ["姓名","年龄","城市","薪资"]
rows = [("张三",28,"北京",4000),("李四",24,"上海",3800),("王五",23,"广州",3400)]
with open(r"d:\b.csv","w") as f:
b_csv = csv.writer(f) # 创建csv对象
b_csv.writerow(headers) # 写入表头
b_csv.writerows(rows) # 写入数据
运行后d盘下自动新建了一个b.csv文件,内容如下:
2.9、os模块
2.9.1、os模块调用操作系统命令
概述:①、os可以帮助我们直接对操作系统进行操作。我们可以直接调用操作系统的可执行文件、命令,直接操作文件、目录等等。
②、通过os.system()可以直接调用系统的命令。
Pycharm控制台出现乱码时的解决办法:Pycharm打开如下路径:File | Settings | Editor | File Encodings ,将Global Encoding对应的utf-8改为gbk或将gbk改为utf-8即可,改完后记得点击应用(Apply)。
实操1:通过os.system()启动系统的计算机应用。
import os
os.system("calc.exe")
实操2:通过os.system()调用系统的ping命令。
import os
os.system("ping baidu.com")
运行结果如下:
实操3:通过os.system()运行任意一个自己按照的应用程序(这里我启动的是qq音乐)。
import os
# 双引号内写入应用的完整路径和文件名即可
os.system(r"D:\qqMusic\QQMusic2005.08.07.38\QQMusic.exe")
运行结果如下:
提示:只要没有关闭程序,打开的应用就会一直运行,如果手动暂停了程序,应用也会被关闭。
2.9.2、os模块对文件和目录的操作
概述:我们可以通过前面讲的文件对象实现对于文件内容的读写操作。如果,还需要对文件和目录做其他操作,可以使用os和os.path模块。
os常用的操作文件方法:
方法名 | 描述 |
---|---|
remove(path) | 删除指定的文件 |
rename(src,dest) | 重命名文件或目录 |
stat(path) | 返回文件的所有属性 |
listdir(path) | 返回path目录下的文件和目录列表 |
os
模块下关于目录操作的相关方法:
方法名 | 描述 |
---|---|
mkdir(path) | 创建目录 |
makedirs(path1/path2/path3/...) | 创建多级目录 |
rmdir(path) | 删除目录 |
removedirs(path1/path2...) | 删除多级目录 |
getcwd() | 返回当前工作目录:current work dir |
chdir(path) | 把path设为当前工作目录 |
walk() | 遍历目录树 |
sep | 当前操作系统所使用的路径分隔符 |
实操1:通过os打印基本信息。
import os
print(os.name) #windows-->nt linux-->posix
print(os.sep) #windows-->\ linux-->/
# repr()的作用是将字符信息转为字符串
print(repr(os.linesep)) #windows-->\r\n linux-->\n
运行结果如下:
实操2:通过案例了解repr()的作用。
strTest = "我是字符信息"
print(strTest)
print(repr(strTest))
运行结果如下:
实操3:获取当前文件的信息。
import os
print(os.stat("test31.py"))
运行结果如下:
2.9.3、os.path模块
概述:os.path模块提供了目录相关(路径判断、路径切分、路径连接、文件夹遍历)的操作。
方法 | 描述 |
---|---|
isabs(path) | 判断path是否绝对路径 |
isdir(path) | 判断path是否为目录 |
isfile(path) | 判断path是否为文件 |
exists(path) | 判断指定路径的文件是否存在 |
getsize(filename) | 返回文件的大小 |
abspath(path) | 返回绝对路径 |
dirname(p) | 返回目录的路径 |
getatime(filename) | 返回文件的最后访问时间 |
getmtime(filename) | 返回文件的最后修改时间 |
walk(top,func,arg) | 递归方式遍历目录 |
join(path,*paths) | 连接多个path |
split(path) | 对路径进行分割,以列表形式返回 |
splitext(path) | 从路径中分割文件的扩展名 |
实操1:练习上述方法。
import os.path
#################获得目录、文件基本信息######################
print(os.path.isabs("d:1.txt")) # 是否绝对路径
print(os.path.isdir("d:1.txt")) # 是否目录
print(os.path.isfile("d:1.txt")) # 是否文件
print(os.path.exists("d:1.txt")) # 文件是否存在
print(os.path.getsize("d:1.txt")) # 文件大小
print(os.path.abspath("d:1.txt")) # 输出绝对路径
print(os.path.dirname("d:/1.txt")) # 输出所在目录
########获得创建时间、访问时间、最后修改时间##########
print(os.path.getctime("d:1.txt")) # 返回创建时间
print(os.path.getatime("d:1.txt")) # 返回最后访问时间
print(os.path.getmtime("d:1.txt")) # 返回最后修改时间
################对路径进行分割、连接操作####################
path = os.path.abspath("d:1.txt") # 返回绝对路径
print(os.path.split(path)) #返回元组:目录、文件
print(os.path.splitext(path)) #返回元组:路径、扩展名
print(os.path.join("aa","bb","cc")) #返回路径:aa/bb/cc
运行结果如下:
实操2:列出当前工作中所有后缀为.py的文件名。
import os
import os.path
# 获取当前文件的工作目录
path_Work = os.getcwd()
# 获取当前文件的工作目录中所有的文件和目录名
name_All = os.listdir(path_Work)
# 通过循环遍历所有的名字
for name_File in name_All:
# 对每个名字从右侧开始找用于标识后缀的.
pos = name_File.rfind(".")
# 通过字符串切片将最右侧.后的内容与指定后缀名py做对比
if name_File[pos+1:] == "py":
print(name_File)
运行结果如下:
实操3:通过列表推导式获取当前工作目录中所有以.py为后缀的文件名。
import os
import os.path
path = os.getcwd()
# endswith()是python基础中提到的查找字符串中是否以指定字符串结尾
name_list = [ py_name for py_name in os.listdir(path) if py_name.endswith(".py")]
print(name_list)
运行结果如下:
2.9.4、walk()递归遍历所有文件和目录
概述:os.walk()方法是一个简单易用的文件、目录遍历器,可以帮助我们高效的处理文件、目录方面的事情。
语法:
os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
语法解析:
top:指要遍历的目录。
topdown是一个可选参数,如果为True,先遍历top指定的目录再遍历其子目录;如果为False,先遍历子目录再遍历top指定的目录。
os.walk()方法将返回三元组:( root,dirs,files )
root:指当前正在遍历的文件夹本身;
dirs:是一个列表,包含该文件夹中所有的目录名;
files:是一个列表,包含文件夹中所有的文件名;
实操:通过os.walk()获取并打印当前工作目录中所有的目录名和文件名。
import os
path = os.getcwd()
lis_file = os.walk(path,topdown=True)
for root,dirs,files in lis_file:
for name in dirs:
# 拼接完整的路径
# print(os.path.join(root,name))
# 只打印目录名
print(name)
for name in files:
# 拼接完整的路径
# print(os.path.join(root,name))
# 只打印文件名
print(name)
运行结果如下:
2.10、shutil模块(用于拷贝和压缩)
2.10.1、shutil模块实现拷贝
概述:①、模块是python标准库中提供的,主要用来做文件和文件夹的拷贝、移动、删除等;还可以做文件和文件夹的压缩、解压缩操作。
②、os模块提供了对目录或文件的一般操作。shutil 模块作为补充,提供了移动、复制、压缩、解压等操作,这些功能os模块都没有提供。
实操1:将d盘中的1.txt文件拷贝到d盘中的test_file目录中并重命名为new1.txt文件。
import shutil
shutil.copyfile(r"d:\1.txt",r"d:\test_file\new1.txt")
执行程序后可以发现d盘中test_file目录中新增了一个名为new1.txt的文件。
实操2:通过shutil模块实现递归拷贝。
提示:shutil模块想实现递归拷贝需要通过shutil.copytree(a,b)实现,a表示被拷贝的父目录,b表示拷贝的路径和名字。
实操前需要在d盘新建一个名为1的目录,然后再1目录中创建名为2的目录,然后进入2目录创建名为3的目录,接下来就可以进行实操了。
import shutil
shutil.copytree(r"d:\1",r"d:\test_file\one")
此时,d盘中test_file目录中就存在了如下目录:D:\test_file\one\2\3
实操3:在实操2的基础上,如果目录中有很多后缀为.py和.html的文件,但我们并不想将这些文件拷贝到我们的目标目录,操作如下:
import shutil
# 如果有不需要拷贝的文件可以通过ignore_patterns方法指定,这里结合了通配符进行文件指定
shutil.copytree(r"d:\1",r"d:\test_file\one",shutil.ignore_patterns("*.html","*.py"))
提示:如果拷贝的目标处已经存在了同名文件目录将会报错,所以使用shutil进行文件或目录拷贝时要避免重名。
2.10.2、shutil模块实现压缩和解压缩
概述:①、想利用shutil模块实现压缩需要用到make_archive()方法,该方法接收三个参数,第一个参数为目录压缩后压缩文件放置的路径,第二个参数用于选择压缩,第三个参数为需要压缩的文件。
②、想利用shutil模块实现解压缩需要用到unpack_archive()方法,该方法接收两个参数,第一个参数为需要解压缩的文件,第二个参数为解压缩后的文件放置的路径。
③、除shutil模块能实现压缩和解压缩外,python还提供了zipfile用于压缩和解压缩,与shutil模块的不同是zipfile可以同时指定多个文件压缩到同一个压缩文件中。
实操1:利用shutil模块对d盘的test_file目录进行压缩和解压缩。
import shutil
# 对文件进行压缩:将d:\test_file目录进行压缩并将压缩文件放置到d:\test_file\下取名为压缩后的test_file (压缩文件后缀默认为zip)
shutil.make_archive(r"d:\test_file\压缩后的test_file","zip",r"d:\test_file")
# 对压缩文件进行解压缩:将压缩文件d:\test_file\压缩后的test_file.zip进行解压,解压到d:\test_file\one\2\3目录下
shutil.unpack_archive(r"d:\test_file\压缩后的test_file.zip",r"d:\test_file\one\2\3")
实操2:利用zipfile对文件进行压缩和解压缩。
import zipfile
# 通过zipfile将文件和目录压缩到同一个压缩文件中
# 创建了一个名为a.zip的新文件,并将ZipFile对象赋值给了变量a
a = zipfile.ZipFile(r"d:\test_file\a.zip","w")
# 将d:\test_file\2.jpg文件添加到a.zip压缩文件中,并将其放在压缩文件的根目录并取名为2.jpg
a.write(r"d:\test_file\2.jpg","2.jpg")
# 将\test_file\new1.txt文件添加到a.zip压缩文件中,并将其放在压缩文件的根目录下的newFile目录中并取名为new1.txt
a.write(r"d:\test_file\new1.txt",r"\newFile\new1.txt")
# 将d:\test_file\one目录添加到a.zip压缩文件中,并将其放在压缩文件的根目录中并取名为one
a.write(r"d:\test_file\one","one")
# 与open()一样,使用完成后一定要将其关闭
a.close()
# 对压缩文件进行解压缩
# 打开d盘下test_file目录中名为a.zip的ZIP文件,使用"r"模式
a2 = zipfile.ZipFile(r"d:\test_file\a.zip","r")
# 提取ZIP文件中的所有文件并保存到D:\test_file\one\2目录下
a2.extractall(r"D:\test_file\one\2")
a2.close()
3、模块
3.1、初始模块
概述:①、Python程序由模块组成。一个模块对应python源文件,一般后缀名是:.py ;
②、模块由语句组成。运行Python程序时,按照模块中语句的顺序依次执行;
③、语句是Python程序的构造单元,用于创建对象、变量赋值、调用函数、控制语句等。
标准库模块:
与函数类似,模块也分为标准库模块和用户自定义模块。
Python标准库提供了操作系统功能、网络通信、文本处理、文件处理、数学运算等基本的功能。比如:random(随机数)、math(数学运算)、time(时间处理)、file(文件处理)、os(和操作系统交互)、sys(和解释器交互)等。
另外,Python还提供了海量的第三方模块,使用方式和标准库类似。功能覆盖了我们能想象到的所有领域,比如:科学计算、WEB开发、大数据、人工智能、图形系统等。
模块化编程的原因:
①、便于将一个任务分解成多个模块,实现团队协同开发,完成大规模程序;
②、实现代码复用。一个模块实现后,可以被反复调用;
③、可维护性增强。
3.2、模块化编程的步骤
模块化编程的一般流程:
①、设计API,进行功能描述;
②、编码实现API中描述的功能;
③、在模块中编写测试代码,并消除全局代码。
④、使用私有函数实现不被外部客户端调用的模块函数。
模块中的API和功能要点:
概述:①、API(Application Programming Interface 应用程序编程接口)是用于描述模块中提供的函数和类的功能描述和使用方式描述。
②、模块化编程中,首先设计的就是模块的API(即要实现的功能描述),然后开始编码实现API中描述的功能。最后,在其他模块中导入本模块进行调用。
③、可以通过help(模块名)查看模块的API。一般使用时先导入模块 然后通过help函数查看。
实操1:通过help()查看os模块的API。
import os
help(os)
运行结果如下:
实操2:设计一个计算学生排名的API(提示:由于我们的文档说明中使用到了中文,所以前两行的编码说明不能缺少,否则会报编码错误)。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
本模块用于计算学生成绩排名
'''
college = "Data science"
def waForSpecialized(*weight,**specialScore):
"""
将传入的学生不同科目的专业成绩和各科目对应的权重计算专业课的加权平均分
specialScore:学生不同专业课的成绩,用字典接收
weight:学科成绩权重,不同的学科权重也存在差异,用元组接收
"""
pass
def waForCultural(*weight,**culScore):
"""
将传入的学生不同科目的文化成绩和各科目对应的权重计算文化课的加权平均分
culScore:学生不同文化课的成绩,用字典接收
weight:学科成绩权重,不同的学科权重存在差异,用元组接收
"""
pass
def waForStudent(actualSpecial,actualCultural):
"""
该方法用于计算学生的总加权平均分
actualSpecial表示进行计算后的专业课加权平均分
actualCultural表示进行计算后的文化课加权平均分
"""
def rangking(**studentScore):
"""
用传入的学生加权平均分对学生成绩进行排名
studentScore用于接收不同学生的加权平均分
"""
pass
完成以上API后,我们进入其他任意.py文件导入该模块,再通过.__doc__就可以查看我们定义的API信息了。
在其他同级目录的python文件中写入如下内容就可以查看模块信息及其模块中的方法信息了:
import ranking
print("模块信息:{}".format(ranking.__doc__))
print("模块中rangking方法的信息:{}".format(ranking.rangking.__doc__))
运行结果如下:
模块测试:
概述:每个模块都有一个名称,通过特殊变量__name__可以获取模块的名称。在正常情况下,模块名字对应源文件名。 仅有一个例外,就是当一个模块被作为程序入口时(主程序、交互式提示符下),它的__name__的值为__name__。我们可以根据这个特点,将模块源代码文件中的测试代码进行独立的处理。
实操3:在实操2的基础上通过实操了解模块的测试(重点是最后的if判断)。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
本模块用于计算学生成绩排名
'''
college = "Data science"
def waForSpecialized(*weight,**specialScore):
"""
将传入的学生不同科目的专业成绩和各科目对应的权重计算专业课的加权平均分
specialScore:学生不同专业课的成绩,用字典接收
weight:学科成绩权重,不同的学科权重也存在差异,用元组接收
"""
pass
def waForCultural(*weight,**culScore):
"""
将传入的学生不同科目的文化成绩和各科目对应的权重计算文化课的加权平均分
culScore:学生不同文化课的成绩,用字典接收
weight:学科成绩权重,不同的学科权重存在差异,用元组接收
"""
pass
def waForStudent(actualSpecial,actualCultural):
"""
该方法用于计算学生的总加权平均分
actualSpecial表示进行计算后的专业课加权平均分
actualCultural表示进行计算后的文化课加权平均分
"""
def rangking(**studentScore):
"""
用传入的学生加权平均分对学生成绩进行排名
studentScore用于接收不同学生的加权平均分
"""
return studentScore
# 测试代码都应该做判断,防止模块调用后执行了测试代码
# 只有自身被调用时__name的值才为__main__
if __name__ == "__main__":
print(rangking(name="张三",age=80))
运行结果如下:
其他文件调用模块后的代码为:
import ranking
print("模块信息:{}".format(ranking.__doc__))
print("模块中rangking方法的信息:{}".format(ranking.rangking.__doc__))
运行结果如下:
通过实操可以发现,通过if __name__ == __main__判断的内容,没有被其他文件调用时运行,有效的防止了代码混乱。
3.3、模块导入
概述:模块化设计的好处之一就是“代码复用性高”。写好的模块可以被反复调用,重复使用。模块导入的本质就是“在本模块中使用其他模块”。
3.3.1、import...导入
语法:
import 模块名 #导入一个模块
import 模块1,模块2… #导入多个模块
import 模块名 as 模块别名 #导入模块并使用新名字
import加载的模块分为四种类型:
①、使用python编写的代码( .py文件 )
②、已被编译为共享库或DLL的C或C++扩展
③、一组模块的包
④、使用C编写并链接到python解释器的内置模块
提示:①、我们一般通过import
语句实现模块的导入和使用,import本质上是使用了内置函数__import__()。
②、当我们通过import导入一个模块时,python解释器进行执行,最终会生成一个对象,这个对象就代表了被加载的模块。
实操:导入数学模块并为其取一个别名,打印其id及其类型并尝试调用sin方法。
import math as m print(id(m)) print(type(m)) print("sin(30):{}".format(m.sin(30)))
运行结果如下:
3.3.2、from... import...导入
概述:Python中可以使用from...import...导入模块中的成员。
语法:
# 导入模块中的特定成员
from 模块名 import 成员1,成员2,…
# 导入模块中的所有成员
from 模块名 import *
注意:尽量避免 from 模块名 import * 这种写法。*号表示导入模块中所有的不是以 _
开头的名字都导入到当前位置。 但不知道导入了什么名字,这很有可能会覆盖掉之前已经定义的名字。而且可读性较差。一般生产环境中尽量避免使用,学习时无关紧要。
实操:导入math模块中的sin和cos并计算sin(30)和cos(30)
from math import sin,cos
print("sin(30):{}\ncos(30):{}".format(sin(30),cos(30)))
运行结果如下:
总结:
①、import导入的是模块。from...import...导入的是模块中的函数/类。
②、通过import导入模块后,如果要使用模块中的方法需要通过模块名.方法名调用;通过from...import...导入模块中的方法/类后,如果需要使用这些方法/类可以直接进行使用,而不用通过模块名进行调用。
3.3.3、动态导入
概述:语句本质上就是调用内置函数__import__(),我们可以通过它实现动态导入。给__import__()动态传递不同的的参数值,就能导入不同的模块。
s = "math"
# 传入的参数可以为任意的模块名,因此可以实现动态导入
m = __import__(s)
print(m.pi)
运行结果如下:
注意:一般不建议我们自行使用__import__()导入,其行为在python2和python3中有差异,会导致意外错误。如果需要动态导入可以使用importlib模块。
import importlib
s = "math"
m = importlib.import_module(s)
print(m.pi)
提示:①、当导入一个模块时, 模块中的代码都会被执行。不过,如果再次导入这个模块,则不会再次执行(导入模块更多的时候需要的是定义模块中的变量、函数、对象等。这些并不需要反复定义和执行。“只导入一次import-only-only”就成了一种优化。)。
②、一个模块无论导入多少次,这个模块在整个解释器进程内有且仅有一个实例对象。
test02.py的源代码:
print("test模块被加载了...")
test03.py的源代码:
import test02 #会执行test02模块中的语句
import test02 #不会再执行test02模块中的语句
深入:有时候我们确实需要重新加载一个模块,这时候可以使用:importlib.reload()方法:
import test02
import test02
print("####")
import importlib
importlib.reload(test02)
3.4、包的创建和导入
概念:①、当一个项目中有很多个模块时,需要再进行组织。我们将功能类似的模块放到一起,形成了“包”。本质上,“包”就是一个必须有__init__.py的文件夹。
②、包下面可以包含“模块(module)”,也可以再包含“子包(subpackage)”。就像文件夹下面可以有文件,也可以有子文件夹一样。
如上图,firstPakage就是包,而每个包必须包含__init__.py文件,下面的secondPakage是firstPakage包含的子包,也包含一个__init__.py文件。
实操:模拟导入其他包中文件的方法。
首先创建如下路径:
returnMax.py内容如下:
def getMax(a,b):
return a if a>b else b
接下来通过firstPakage2包里的test.py文件引入firstPakage包中secondPakage子包中的returnMax文件,并使用文件中的getMax方法。(提示:所有的文件我都放在code包中,所以需要引入code)
# 导入方式不同,方法的调用也不一样
import code.firstPakage.secondPakage.returnMax
print(code.firstPakage.secondPakage.returnMax.getMax(2, 5))
from code.firstPakage.secondPakage import returnMax
print(returnMax.getMax(4, 3))
from code.firstPakage.secondPakage.returnMax import getMax
print(getMax(2,3))
运行结果如下:
注意:导入包的本质其实是“导入了包的__init__.py”文件。也就是说,import packgeName 意味着执行了包packgeName下面的__init__.py文件。 这样,可以在__init__.py中批量导入我们需要的模块,而不再需要一个个导入。
__init__.py的核心作用:
①、作为包的标识;
②、导入包实质是执行__init__.py文件,可以在__init__.py文件中做这个包的初始化、以及需要统一执行代码、批量导入。