在我们的日常生活中,软件程序的运行并不都是完美的,经常会出现一些我们意料之外的异常情况。这个时候,我们就需要一些异常处理的机制能够保证在异常发生时,能够正确处理异常,保证用户的使用。
比如,我们要从D盘拷贝一个文件到E盘,拷贝函数的代码只有短短一行语句,但是为了判断能不能拷贝,我们需要考虑多方面因素:这个文件是否存在?E盘的空间够不够?如果文件复制到一半,因系统出现了别的情况导致复制过程中段怎么办?
Python的异常机制提供了非常方便的异常处理方式。
异常:程序在运行过程中出现的非正常现象 异常处理:程序在出现问题时依然可以正确的执行剩余的程序,而不会因为异常而终止程序执行。
如,我们尝试一下用5除以0的这个除数异常:
a = 5 / 0 Traceback:追溯,追根溯源most recent call last:最后一次调用ZeroDivisionError:除法中除数为0异常异常解决的关键:定位 学会找到异常出现的真正位置,找因果关系 比如:出现以下错误
def a(): num = 1 / 0 def b(): a() def c(): b() c()语法结构: try: 被监控的可能引发异常的语句块 except BaseException [as e]:# 或者直接Exception也可以 异常处理语句块
遇到异常会直接跳过异常语句以及之后的语句段,直接跳到相应except块中去执行处理异常语句 如:
# try-except try: print('s1') print(5/0) print('s2') except ZeroDivisionError as e: print('s3') print('异常对象:',e) # 产生的异常对象 print(type(e)) print("You can't divide by zero!") print('s4')【测试】
# 循环输入数字,如果不是数字则处理异常,直到输入88,则结束循环 while True: try: x = int(input("Please input a number:")) print("输入的数字为:",x) if x == 88: print("退出程序!") break except BaseException as e: print("异常!输入的不是一个数字!") print("循环数字输入结束!")在工作中,其实可以尽量多的去捕获异常(按照先子类后父类的顺序),可以在最后增加BaseException,从上到下,从子类到父类 语法结构: try: 被监控的、可能引发异常的语句块 except Exception1: 处理Exception1的语句块 except Exception2: 处理Exception2的语句块 … except BaseException: 处理可能遗漏的异常语句块
【测试】
try: a = input('Please input a dividend:') # 被除数 b = input('Please input a divisor:') # 除数 c = float(a) / float(b) print(c) except ZeroDivisionError: print('异常,不能除以0!') except ValueError: print('异常,不能将字符串转化成数字!') except NameError: print('异常,变量不存在!') except BaseException as e: print(e)
【注】可以通过鼠标选中异常类右键选择’Diagram‘–‘show Diagram’查看异常类的继承关系图:
【测试】
# try-except-else语句 print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True: first_number = input("\nFirst number:") if first_number == 'q': break second_number = input("Second number:") if second_number == 'q': break try: answer = int(first_number) / int(second_number) except ZeroDivisionError: print("You can't divide by 0!") else: print(answer)finally块:无论是否发生异常,都会执行里面的语句,通常用来释放try块中申请的资源 【测试1】
try: a = input('Please input a dividend:') # 被除数 b = input('Please input a divisor:') # 除数 c = float(a) / float(b) except BaseException as e: print(e) else: print(c) finally: print('无论异常发生与否都会执行finally语句!') print('程序结束!')【测试2】
try: f = open('d:/a.txt','r') content = f.readline() print(content) except: print('文件未找到!') finally: print('run in finally!') try: f.close() except BaseException as e: print(e) print('程序执行结束!')【注意一点】 我们一般不把return语句放在异常处理结构中,而是放到方法的最后 如:
def test01(): print('s1') try: x = 3 / 0 except: print('s2') print('异常,除数不能为0!') finally: print('s4') print('s5') return 'e' # 一般不将return语句放到try、except、else、finally块中,会发生一些意想不到的错误,建议放到方法最后! print(test01())异常表格汇总:
异常名称说明ArithmeticError所有数值计算错误的基类AssertionError断言语句失败AttributeError对象没有这个属性BaseException所有异常的基类DeprecationWarning关于被弃用的特征的警告EnvironmentError操作系统错误的基类EOFError没有内建输入,到达EOF标记Exception常规错误的基类FloatingPointError浮点计算错误FutureWarning关于构造将来语义会有改变的警告GeneratorExit生成器发生异常来通知退出ImportError导入模块 / 对象失败IndentationError缩进错误IndexError序列中没有此索引IOError输入 / 输出操作失败KeyboardInterrupt用户中断执行(通常是^C)KeyError映射中没有这个键LookupError无效数据查询的基类MemoryError内存溢出错误NameError未声明 / 初始化对象(没有属性)NotImplementedError尚未实现的方法OSError操作系统错误OverflowError数值运算超出最大限制OverflowWarning旧的关于自动提升为长整型long的警告PendingDeprecationWarning关于特性将会被废弃的警告ReferenceError弱引用试图访问已经垃圾回收了的对象RuntimeError一般的运行时错误RuntimeWarning可疑的运行时行为的警告StandardError所有内建标准异常的基类StopIteration迭代器没有更多的值SyntaxErrorpython语法错误SyntaxWarning可疑的语法警告SystemError一般的解释器系统错误SystemExit解释器请求退出TabErrorTab和空格混用TypeError对类型无效的操作UnboundLocalError访问未初始化的本地变量UnicodeDecodeErrorUnicode解码时的错误UnicodeEncodeErrorUnicode编码时的错误UnicodeErrorUnicode相关的错误UnicodeTranslateErrorUnicode转换时错误UserWarning用户代码生成的警告ValueError传入无效的参数Warning警告的基类WindowsError系统调用失败ZeroDivisionError除(或取模)零(所有数据类型)用于打印一些异常信息 【测试1】
import traceback try: print('s1') num = 3 / 0 except: traceback.print_exc()【测试2:将异常信息输出到指定文件中】
import traceback # 将异常信息输出到指定文件中 try: print('s2') num = 5 / 0 except: with open('d:/Python_projects/test_Exception/test11.txt','a') as f: traceback.print_exc(file = f)
一般为运行时异常,通常继承Exception或其子类即可(命名一般以Error、Exception为后缀) 自定义异常由raise语句主动抛出
【测试】
class AgeError(Exception): # 继承Exception类 def __init__(self,errorInfo): Exception.__init__(self) self.errorInfo = errorInfo def __str__(self): return str(self.errorInfo) + '年龄错误!应在1~100之间' if __name__ == '__main__': # 如果为True,则模块是作为独立文件运行,可以执行测试代码 age = int(input('Please input an age:')) if age < 1 or age > 100: raise AgeError(age) # 抛出自定义异常 else: print("正常的年龄!",age)语法格式: 变量名 = open(文件名 [,打开方式])
打开方式: 【注】
如果打开方式中没有增加模式“b”,则默认创建文本文件对象,处理的基本单元为“字符”如果是二进制模式“b”,则创建的是二进制文件对象,处理的基本单元是“字节”创建文件对象–写入数据–关闭文件对象
f = open(r'plp.txt','a') # a--追加 s = 'ashgffk\neer\n' f.write(s) f.close()【注】写入中文的时候注意会出现乱码
常用的编码介绍: 一般项目都会使用 UTF-8。unicode 中虽然汉字是两个字节,UTF-8 中汉字是 3 个字节。但是互联网中一个网页也包含了大量的英文字母,这些英文字母只占用 1 个字节,整体占用空间,UTF-8 仍然由于 Unicode。
windows 操作系统默认的编码是 GBK,Linux 操作系统默认的编码是 UTF-8。当我们用 open()时,调用的是操作系统打开的文件,默认的编码是 GBK。
如:
f = open(r'plp.txt','a') s = '你们好我很喜欢编程\n真的\n' f.write(s) f.close()
借助异常机制,使得文件能够正常关闭
try: f = open(r"aa.txt","a") str = "ggg" f.write(str) except BaseException as e: print(e) finally: f.close()结合上面的异常处理,由于异常处理中finally块异常如何怎么都会执行,释放资源。另一种方法,我们还可以用with上下文更方便实现释放文件资源的操作,不用再手动关闭文件了
语法结构: with context_expr [as var]: 语句块
with代码执行完后,自动会还原原来的现场或上下文,不论是否有异常,总能保证资源正常释放,极大简化工作。 【测试1】
with open('d:/Python_projects/test_Exception/test11.txt','r') as f: content = f.readline() print(content) print('程序结束!')【测试2】
file_name = 'programming.txt' with open(file_name,'w') as file_object: file_object.write("I love learning Python and programming!\n") with open(file_name,'a') as file_object: file_object.write("I will try my best!\n") file_object.write("Hey boy!\n") file_object.write("Welcome this world!\n") with open(file_name,'r') as file_object: for line in file_object: print(line)【测试】
with open('pi_digits.txt') as file_object: contents = file_object.read() print(contents.rstrip()) # rstrip 去除字符串末尾的空白 print("output ...") print('\n*******************\n') file_path = r'D:\test_file\data_set.txt' # 原始路径 前面必须加r(在单引号前面) with open(file_path,encoding='utf-8') as file_object: # 注意文本文档的编码 c = file_object.read() print(c) print('\n*********按行读取一个文件**********\n') with open(r'programming.txt','r') as f: while True: fragment = f.readline() if not fragment: break else: print(fragment, end = "") print('\n*********使用迭代器(每次返回一行)读取文本文件**********\n') # 逐行读取 file_path = r'D:\test_file\app_set.txt' file_name = 'app_set.txt' with open(file_path,encoding='utf-8') as file_object: for line in file_object: print(line,end = "") print('\n********包含文件各行内容的列表***********\n') # 包含文件各行内容的列表 file_name = 'pi_digits.txt' with open(file_name) as file_object: lines = file_object.readlines() for line in lines: print(line.rstrip()) print(lines) # 列表 print('\n*******************\n') file_name = 'plp.txt' with open(file_name) as file_object: lines = file_object.readlines() pi_string = '' for line in lines: pi_string += line.strip() # strip 删除位于左边空格 print(pi_string) print(len(pi_string))【小练习:给文件每行内容加行号】 文件programming.txt 【测试enumerate】:枚举 和索引关联起来
# enumerate测试 a = ['我我我','你你你\n','她她它\n'] b = enumerate(a) # 枚举 和索引关联起来 print(a) print(list(b)) # 给文件每行内容加行号 with open(r'programming.txt','r',encoding = 'utf-8') as f: lines = f.readlines() lines = [line.rstrip() + " #" + str(index+1) + '\n' for index,line in enumerate(lines)] with open(r'programming.txt','w',encoding = 'utf-8') as f: f.writelines(lines)【例】
with open('boy.jpg','rb') as f: with open('boy_copy.jpg','wb') as w: for line in f.readlines(): w.write(line) print("图片拷贝完成......")【总结表格】
1.文件对象的属性:
属性说明name返回文件的名字mode返回文件的打开模式closed若文件被关闭则返回True2.文件对象的打开模式:
模式说明r读模式w写模式a追加模式b二进制模式(可与其他模式组合)+读写模式(可与其他模式组合)3.文件对象的常用方法:
方法名说明read([size])从文件中读取size个字节或字符的内容返回,默认读取到文件末尾readline()从文本文件中读取一行内容readlines()把文本文件中每一行都作为独立的字符串对象,并将这些对象放入列表返回write(str)将字符串str内容写入文件writelines(s)将字符串列表s写入文件,不添加换行符seek(offset [,whence])把文件指针移动到新的位置tell()返回文件指针的当前位置truncate([size])不论指针在什么位置,只留下指针前size个字节的内容,其余全部删除;如果没有传入size,则把指针当前位置一直到文件末尾的内容全部删除flush()把缓冲区的内容写入文件,但不关闭文件close()把缓冲区内容写入文件,同时关闭文件,释放文件对象相关资源【注】 seek(offset [,whence])方法:把文件指针移动到新的位置
offset: 为正:往文件内容结束方向移动 为负:往文件内容开始方向移动whence: 0:从文件头开始计算(默认) 1:从当前位置开始计算 2:从文件尾开始计算【测试:文件指针与seek方法】 【查看文件指针的初始位置】
with open('programming.txt','r',encoding = 'utf-8') as f: print('文件名:{0}'.format(f.name)) print(f.tell())【读取了一行后,文件指针的当前位置】 【文件指针偏移】
Python中,一切都是对象,也就是一个“存储数据的内存块”。 我们想要将这些“内存块的数据”保存到硬盘上,或通过网络传输到其他计算机上。(通过转化成一系列的串(0101011…)进行传输(序列化),再拿出来还原回对象进行访问(反序列化)) 使用pickle实现序列化和反序列操作
使用:
pickle.dump(obj , file):将对象obj持久化地存到文件file中 obj:要被序列化的对象 file:存储的文件pickle.load(file): 从file读取数据,反序列化成对象【例】
import pickle a1 = 'Stefan' a2 = 1996 a3 = [20,30,40] with open(r'data.dat','wb') as f: pickle.dump(a1,f) pickle.dump(a2,f) pickle.dump(a3,f) with open(r'data.dat','rb') as f: b1 = pickle.load(f) b2 = pickle.load(f) b3 = pickle.load(f) print(b1);print(b2);print(b3) print(id(a1)) print(id(b1))CSV文件(Comma Separated Values),逗号分隔符文本格式,常用于数据交换、Excel文件、数据库数据的导入导出等等工作。
和Excel文件相比,CSV文件:
值没有类型,所有值均为字符串不能指定字体颜色等样式不能指定单元格宽高,不能合并单元格没有多个工作表不能嵌入图像图表Python标准库提供csv模块读取和写入csv文件 如,建立一个excel文件 保存为csv文件,并用记事本打开 【测试】
import csv # csv文件的读取 with open('sal.csv','r') as f: a_csv = csv.reader(f) print(list(a_csv)) print('--------------------') f.seek(0) # 注意这里要把文件指针移回开头,否则只会遍历一次 for row in a_csv: print(row) # csv文件的写入 with open('mess.csv','w') as f: b_csv = csv.writer(f) b_csv.writerow(['Number','Name','Age']) b_csv.writerow(['1001','Kate',19]) c = [['1011','Tim',22],['1200','Kate',30]] b_csv.writerows(c)读取结果: 写入结果:
直接对操作系统进行操作,调用os的可执行文件、命令等,系统运维的核心基础。
os.system:直接调用系统命令(电脑上的程序) 如, 直接调用记事本程序: 调用注册表regedit: 查看网络交互情况:
【常用方法总结】 【操作文件】 【操作目录】 【例】
#coding=utf-8 import os # 获取文件和文件夹相关信息 print(os.name) # windows——>nt;Linux和unix——>posix print(os.sep) # windows——>反斜杠;Linux和unix——>正斜杠 print(repr(os.linesep)) # windows——>\r\n;Linux和unix——>\n print(os.stat('test_os_01.py')) # 访问文件信息 # 关于工作目录的操作 print(os.getcwd()) # 查看当前工作空间 # os.chdir('d:') # 改变当前的工作目录 # os.mkdir('书籍') # 创建工作目录 # 创建目录、创建多级目录、删除 # os.mkdir('书籍') # 创建目录 # os.rmdir('书籍') # 删除目录(相对于当前工作空间下) # os.makedirs('Movies/America/Agent Carter') # 创建多级目录 # os.removedirs('Movies/America/Agent Carter') # 注:只能删除空目录!! # os.makedirs('../音乐/大陆/毛不易') # ../:指的是上级目录 # os.rename('Movies','M') # 重命名 # dirs = os.listdir('Movies') # print(dirs) # 输出所有的子目录(注:只能获取一级子目录!!)【小点】repr()函数: 将对象转化为供解释器读取的形式,也就是说,把一个对象(obj)转换成一个可打印的字符串,它特点是对于包含着转义字符的字符串,也能完整打印出来。
【测试】
#coding=utf-8 import os import os.path from os import path print('\n****路径判断****\n') print('是否是绝对路径:{0}'.format(path.isabs(r'D:\Python_projects\test_file\programming.txt'))) print('是否是目录:{0}'.format(path.isdir(r'D:\Python_projects\test_file\programming.txt'))) print('是否是文件:{0}'.format(path.isfile(r'D:\Python_projects\test_file\programming.txt'))) print('文件是否存在:{0}'.format(path.exists(r'D:\Python_projects\test_file\programming.txt'))) print('\n****获得文件基本信息****\n') print('文件大小:{0}字节'.format(path.getsize(r'D:\Python_projects\test_file\programming.txt'))) print('绝对路径:{0}'.format(path.abspath(r'D:\Python_projects\test_file\programming.txt'))) print('目录的路径:{0}'.format(path.dirname(r'D:\Python_projects\test_file\programming.txt'))) print('\n****获得时间信息****\n') # 距离Unix时间点过了多少毫秒 print(path.getctime(r'D:\Python_projects\test_file\programming.txt')) # 文件创建时间 print(path.getatime(r'D:\Python_projects\test_file\programming.txt')) # 文件最后访问时间 print(path.getmtime(r'D:\Python_projects\test_file\programming.txt')) # 文件最后修改时间 print('\n****对路径的操作****\n') p = path.abspath(r'D:\Python_projects\test_file\programming.txt') print(path.split(p)) # 路径切分 print(path.splitext(p)) # 按“.”切分(分割扩展名) print(path.join('aa','bb','cc')) # 连接多个路径【练习:列出工作目录下所有的.py文件,并输出文件名】
#coding=utf-8 # 列出工作目录下所有的.py文件,并输出文件名 import os path = os.getcwd() file_list = os.listdir(path) for filename in file_list: if filename.endswith('py'): print(filename) print('\n****用推导式方法****\n') file_list2 = [filename for filename in os.listdir(path) if filename.endswith('py')] for f in file_list2: print(f)os.walk()方法: 返回一个3个元素的元组,(dirpath,dirnames,filenames)
dirpath:指定目录的路径dirnames:目录下所有文件夹filenames:目录下的所有文件【例】
#coding=utf-8 import os os.chdir(r'D:\人工智能\专业相关') path = os.getcwd() list_files = os.walk(path) for dirpath,dirnames,filenames in list_files: for dir in dirnames: print(os.path.join(dirpath,dir)) for file in filenames: print(os.path.join(dirpath,file))【测试】
#coding=utf-8 import shutil import zipfile # 拷贝 shutil.copyfile('1.txt','1_copy.txt') shutil.copytree('movie/America','电影') # 注:当文件已存在时,无法创建该文件 shutil.copytree('movie/America','电影',ignore = shutil.ignore_patterns('*.txt','*.html')) # 拷贝时可以选择性忽略一些类型的文件进行拷贝 # 压缩、解压缩 # shutil.make_archive('压缩包所在位置及名字','压缩格式(一般zip)','压缩的目录内容') shutil.make_archive('音乐/movie','zip','电影/学习') # 压缩 z1 = zipfile.ZipFile('a.zip','w') z1.write('1.txt') z1.write('1_copy.txt') z1.close() # 解压缩 z2 = zipfile.ZipFile('d:/a.zip'.'r') z2.extractall('电影') # 设置解压位置 z2.close()【测试结果】
与函数类似,模块也分为标准库模块和用户自定义模块。 Python 标准库提供了操作系统功能、网络通信、文本处理、文件处理、数学运算等基本的功能。比如:random(随机数)、math(数学运算)、time(时间处理)、file(文件处理)、os(和操作系统交互)、sys(和解释器交互)等。
另外,Python 还提供了海量的第三方模块,使用方式和标准库类似。功能覆盖了我们能想象到的所有领域,比如:科学计算、WEB 开发、大数据、人工智能、图形系统等。
模块化编程有如下几个重要优势:
便于将一个任务分解成多个模块,实现团队协同开发,完成大规模程序实现代码复用。一个模块实现后,可以被反复调用。可维护性增强。模块化编程的一般流程:
设计 API,进行功能描述。编码实现 API 中描述的功能。在模块中编写测试代码,并消除全局代码。使用私有函数实现不被外部客户端调用的模块函数。API(Application Programming Interface 应用程序编程接口)是用于描述模 块中提供的函数和类的功能描述和使用方式描述。
模块化编程中,首先设计的就是模块的 API(即要实现的功能描述),然后开始编码实现 API 中描述的功能。最后,在其他模块中导入本模块进行调用。我们可以通过help(模块名)查看模块的API。一般使用时先导入模块 然后通过help函数查看。
【help查看】
import math print(math.sqrt(81)) help(math)【Python的API文档中查看】 【例】 自己定义一个模块,撰写文档字符串,然后去调用该模块,查看文档字符串等信息
Salary.py文件
#coding=utf-8 ''' 用于计算公司员工的薪资 ''' company = 'StarWar' def yearSalary(monthSalary): ''' 根据传入的月薪的值,计算出年薪(monthSalary * 12) :param monthSalary: 月薪 :return: 年薪 ''' return monthSalary * 12 def daySalary(monthSalary): ''' 根据传入的月薪值,计算日工资,一个月按22.5天计算(国家规定工作日) :param monthSalary: 月薪 :return: 日工资 ''' return monthSalary / 22.5 if __name__ == '__main__': # 自己测试用 模块独立运行 print(yearSalary(4000)) print(daySalary(4000))test_Salary.py文件
#coding=utf-8 import Salary # __doc__获取模块的文档字符串 print(Salary.__doc__) print(Salary.daySalary.__doc__) print(Salary.yearSalary.__doc__) print('模块名:',Salary.__name__)语法格式: import 模块名 # 导入一个模块 import 模块1,模块2… # 导入多个模块 import 模块名 as 模块别名 # 导入模块并使用别名
本质:生成了一个对象(module类),并被变量使用。
import 加载的模块分为四个通用类别:
使用 python 编写的代码(.py 文件)已被编译为共享库或 DLL 的 C 或 C++扩展包好一组模块的包使用 C 编写并链接到 python 解释器的内置模块【注】
当导入一个模块时, 模块中的代码都会被执行。不过,如果再次导入这个模块,则不会再次执行。(import-only-once)一个模块无论导入多少次,这个模块在整个解释器进程内有且仅有一个实例对象。 import math as m print(id(m)) print(type(m)) print(m.sqrt(16)) import math as ma # 无论导入多少次都只生成一个实例对象 print(id(ma))可导入模块中的成员(函数 / 类) 如:
#coding=utf-8 from math import sin print(sin(3.14)) # 可直接写函数名,不用再”.“去调用了import语句的本质:调用内置函数__import __() 实现动态导入,给__import __() 动态传递不同的的参数值,就能导入不同的模块。
使用importlib模块要更好:
#coding=utf-8 import importlib a = importlib.import_module("math") b = importlib.import_module("time") print(a.pi) print(b.time())如需要重新加载模块,用importlib重新加载模块 importlib.reload()方法
【注】 导入包的本质其实是“导入了包的__init__.py”文件。也就是说,”import pack1”意味着执行了包 pack1 下面的__init__.py 文件。 这样,可以在__init__.py 中批量导入我们需要的模块,而不再需要一个个导入。
init.py 的三个核心作用:
作为包的标识,不能删除。用来实现模糊导入导入包实质是执行__init__.py 文件,可以在__init__.py 文件中做这个包的初始化、以及需要统一执行代码、批量导入。如果是子包内的引用,可以按相对位置引入子模块 ,比如要引用同工作目录包下你上级目录中的包:
from … import module_A #…表示上级目录 .表示同级目录from . import module_A2 #.表示同级目录当我们导入某个模块文件时, Python 解释器去哪里找这个文件呢?只有找到这个文件才能读取、装载运行该模块文件。它一般按照如下路径寻找模块文件(按照顺序寻找,找到即停不继续往下寻找):
内置模块当前目录程序的主目录pythonpath 目录(如果已经设置了 pythonpath 环境变量)标准链接库目录第三方库目录(site-packages 目录).pth 文件的内容(如果存在的话)sys.path.append()临时添加的目录当任何一个 python 程序启动时,就将上面这些搜索路径(除内置模块以外的路径)进行收集,放到 sys 模块的 path 属性中(sys.path)。
【测试】
import sys print(sys.path)