Python高级语法之——模块与包、异常处理、迭代器与生成器、文件操作
作者:Barranzi_
个人github主页:[github](https://github.com/La0bALanG)
个人邮箱:awc19930818@outlook.com
新时代的铁饭碗:一辈子不管走到哪里都有饭吃(还能吃上热乎的)。——佚名
免责声明:
本系列笔记撰写初衷就是为了分享个人知识以及个人学习历程中的感悟及思考,所涉及到的内容`仅供学习与交流`,请勿用作`非法或商业用途`!由此引发的任何法律纠纷`后果自负`,与作者本人无关!
版权声明:
未经作者本人授权,禁止转载!请尊重原创!
注:本文所有代码、案例测试环境:1.Linux -- 系统版本:Ubuntu20.04 LTS 2.windows -- 系统版本:WIN10 64位家庭版
模块与包
模块module
-
定义:包含一系列数据、函数、类的文件,通常以.py结尾。
-
作用:让一些相关的数据,函数,类有逻辑的组织在一起,使逻辑结构更加清晰,更利于多人协作开发。
-
模块导入语句
-
import – 语法
import 模块名 import 模块名 as 别名- 作用:将某模块整体全部导入到当前模块中
- 使用:模块名.成员
-
from import – 语法
from 模块名 import 成员名 as 别名- 作用:将模块内的一个或多个成员导入到当前模块的作用域中。
- 使用:直接使用对应成员名即可
-
from import * – 语法
from 模块名 import *- 作用:将某模块的所有成员导入到当前模块。
- 注意:模块中以下划线(_)开头的属性,不会被导入,通常称这些成员为隐藏成员。
-
-
模块变量
- __all__变量:定义可导出成员,仅对from xx import *语句有效。
- __doc__变量:文档字符串。
- __file__变量:模块对应的文件路径名。
- __name__变量:模块自身名字,可以判断是否为主模块。
- 当此模块作为主模块(第一个运行的模块)运行时,name__绑定’__main‘,不是主模块,而是被其它模块导入时,存储模块名。
-
模块的加载过程
- 模块路径搜索:由Python解释器自动完成。一般情况下,模块的路径搜索按照如下顺序进行:
- 程序主目录
- 系统PATH目录
- 标准库目录
- 模块预编译:搜索到目标模块后对其编译,生成扩展名为.pyc的同名文件
- 运行
- 模块路径搜索:由Python解释器自动完成。一般情况下,模块的路径搜索按照如下顺序进行:
包package
-
定义:将模块以文件目录的形式进行分组管理
-
作用:让一些相关的模块组织在一起,使逻辑结构更加清晰
-
导入
from 包名 import 模块名 [as 模块新名] from 包名.子包名 import 模块名 [as 模块新名] from 包名.子包名.模块名 import 成员名 [as 属性新名] # 导入包内的所有子包和模块 from 包名 import * from 包名.模块名 import * import 包名 [as 包别名] 需要设置__all__ import 包名.模块名 [as 模块新名] import 包名.子包名.模块名 [as 模块新名] - 包的搜索顺序:按照系统PATH提供的路径进行搜索
- init.py文件:是包内必须存在的文件,在包加载时进行自动调用
- all:记录from 包 import * 语句需要导入的模块
异常处理
- 定义:程序运行时检测到的错误
- 现象:当程序异常发生时,程序会终止运行,并在错误信息中展示出对应的异常发生位置(物理行)
Python编码中常见的错误类型解读
- 名称异常(NameError):变量未定义。
- 类型异常(TypeError):不同类型数据进行运算。
- 索引异常(IndexError):超出索引范围。
- 属性异常(AttributeError):对象没有对应名称的属性。
- 键异常(KeyError):没有对应名称的键。
- 未实现异常(NotImplementedError):尚未实现的方法。
- 异常基类Exception。
异常处理语句
-
语法
try: 可能触发异常的语句 except 错误类型1 [as 变量1]: 处理语句1 except 错误类型2 [as 变量2]: 处理语句2 except Exception [as 变量3]: 不是以上错误类型的处理语句 else: 未发生异常的语句 finally: 无论是否发生异常的语句 -
说明
- as子句用于绑定错误对象的变量,通常用其输出具体的错误信息
- except子句可以存在1个或多个,通常用其捕获某种类型的错误
- else子句只能有一个
- finally子句最多只能有一个,如果不存在except子句,则其必须存在
- 如果为捕获到任何异常,则程序继续运行直至结束
-
raise语句
-
作用:抛出一个错误,让程序进入异常状态
-
目的:在程序调用层数较深时,向主调函数传递错误信息要层层return,比较麻烦,所以人为抛出异常,可以直接传递错误信息。
-
语法:
raise 异常类型
-
自定义异常类
-
目的:提升编码时的规范与约束,为第三方模块及功能添加自定义异常处理信息,方便规范编码
-
说明:自定义异常类必须继承自Exception
-
实现
- 创建自定义异常基类
- 由此基类派生其他异常类,方便管理
- 异常类中依据raise语句抛出异常
-
举个例子
class DemoError(Exception): pass class NameError(DemoError): def __init__(self,a,b): self.a = a self.b = b def __str__(self): return '本模块只接收整型对象' def test(a,b): try: if type(a) != int or type(b) != int: raise NameError(a,b) except Exception as e: print(e) else: if a > b: return a else: return b res = test(1,'2') print(res) ============================================= H:\老安课程串讲资料\文档、代码杂七八\venv\Scripts\python.exe H:/老安课程串讲资料/文档、代码杂七八/demo1001.py 本模块只接收整型对象 None Process finished with exit code 0
迭代器与生成器
迭代
- 定义:每一次对过程的重复称为一次迭代,而每一次迭代得到的结果会作为下一次迭代的初始值。例如:for循环从容器中获取元素
- 迭代器协议:两个方法
- iter
- next
可迭代对象iterable
-
定义:实现了迭代器协议__iter__方法的对象,就是可迭代对象
-
创建语法:
class 可迭代对象: def __iter__(self): ... return 迭代器 -
使用语法:
for 循环变量 in 可迭代对象: 语句块 -
可迭代对象使用原理:
- for循环遍历可迭代对象时,for语句会调用对象中的iter()方法
- iter()方法返回一个实现了next()方法的迭代器对象,该方法将逐一访问容器中的元素
迭代器对象iterator
-
定义:可以被next()函数调用并返回下一个值的对象。
-
迭代器对象创建语法:
class 迭代器对象: def __init__(self,聚合对象): self.聚合对象名 = 聚合对象 def __next__(self): if 没有元素: raise StopIteration return 聚合对象元素 -
说明:聚合对象大部分情况下就是序列或容器对象
生成器generator
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
生成器函数
-
定义:含有yield语句的函数,返回值为生成器对象
-
创建语法:
def 函数名(): ... yield 数据对象 ... -
调用语法:
for 循环变量 in 函数名(): 语句 -
说明:
- 调用生成器函数将返回一个生成器对象,不执行函数体
- yield意为“生产”,“生成”,“产生”
-
生成器函数执行过程:
- 调用生成器函数会自动创建迭代器对象。
- 调用迭代器对象的__next__()方法时才执行生成器函数。
- 每次执行到yield语句时返回数据,暂时离开。
- 待下次调用__next__()方法时继续从离开处继续执行。
-
生成器函数原理:
- 将yield关键字以前的代码放在next方法中。
- 将yield关键字后面的数据作为next方法的返回值。
内置生成器
-
枚举函数enumerate
-
语法:
for 变量 in enumerate(可迭代对象): 语句 for 索引, 元素 in enumerate(可迭代对象): 语句 -
作用:遍历可迭代对象时,可以将索引与元素组合为一个元组。
-
-
zip
-
语法:
for item in zip(可迭代对象1, 可迭代对象2….): 语句 -
作用:将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由最小的可迭代对象决定。
-
生成器表达式
-
定义:用推导式形式创建生成器对象
-
语法:
变量 = (表达式 for 变量 in 可迭代对象 [if 真值表达式])
上下文管理器
-
上下文解析
上下文管理器通常用于对一些资源的操作,主要针对资源的获取与释放。
现在通过一个例子来进一步了解上下文管理器。
举例:pymysql操作数据库时的数据库连接、查询、关闭操作。
class DBConnect(object): def __init__(self): self.connected = False def connect(self): self.connected = True def close(self): self.connected = False def query(self): if self.connected: return '正在查询数据...' else: raise ValueError('数据库未连接') def test(): db = DBConnect() db.connect() print(db.query()) db.close() test()上述代码逻辑相对清晰,创建了一个数据库连接类DBConnect,提供了connect、query、close,即连接,查询,关闭三个常见的数据库操作方法。在客户端代码中,需要查询数据库并做查询结果处理。当然在这个操作之前,需要连接数据库(db.connect())和操作之后关闭数据库连接(db.close())。那么,如果存在很多处类似test内的处理逻辑,连接和关闭这样的方法就需要调用很多遍,显然这并不是一个好的现象。
对于这样的场景,Python中的装饰器可以进行很好的处理。现在我们先编写一个像样的装饰器先看看如何处理的:
class DBConnect(object): def __init__(self): self.connected = False def connect(self): self.connected = True def close(self): self.connected = False def query(self): if self.connected: return '正在查询数据...' else: raise ValueError('数据库未连接') #装饰器函数 def dbconn(fn): def in_(*args,**kwargs): db = DBConnect() db.connect() ret = fn(db, *args, **kwargs) db.close() return ret return in_ @dbconn def test(db=None): print(db.query()) test()我们自定义一个装饰器函数,将所有需要复用的连接及关闭操作封装进装饰器内部,使用装饰器去装饰对应db逻辑,看上去实现了连接、关闭的复用。
但是,此种方式我们也需要事先定义数据库操作的相关资源语句,看起来也并不好,还是不够优雅。
为了解决上述这个问题,Python提供了with语句,来构建对资源进行创建与释放的语法糖。给DBConnect类添加两个魔术方法:
class DBConnect(object): def __init__(self): self.connected = False def connect(self): self.connected = True def close(self): self.connected = False def query(self): if self.connected: return '正在查询数据...' else: raise ValueError('数据库未连接') def __enter__(self): self.connect() return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() def test(db=None): with DBConnect() as db: print(db.query()) test()然后使用with语句管理上下文,如此,彻底实现资源创建及关闭的复用且代码逻辑清晰优雅。
-
定义:实现了上下文管理协议的函数 or 对象就是上下文管理器
-
上下文管理协议
- __enter__方法:进入运行时上下文环境
- __exit__方法:退出运行时上下文环境
-
上下文管理器创建语法
class Contextor: def __enter__(self): ... return self def __exit__(self, exc_type, exc_value, exc_traceback): pass contextor = Contextor() with contextor [as var]: 语句块 -
with语句执行上下文管理器原理总结:
- 执行contextor获取上下文管理器
- 加载上下文管理器的exit()方法以备稍后调用
- 调用上下文管理器的enter()方法
- 如果存在as var子句,则将enter()方法的返回值赋值给var。所以,enter()方法通常直接返回上下文管理器对象的实例self,方便后续调用
- 执行with中的语句块
- 执行完毕,调用上下文管理器中的exit()方法。
- 如果with中的语句退出是由异常触发的,那么该异常的类型type,value及其traceback会作为参数传递给exit(),否则传递三个None
- 如果with中的语句退出是由异常触发的,且exit()的返回值为False,那么这个异常将被重新触发依次
- 如果exit()的返回值等于True,那么这个异常跳过,继续执行后续代码
-
对于上下文的真正理解:当模块中的代码,函数,类中的方法在执行的时候,可能会处于不同的环境下,在不同的环境下调用函数或方法时,可能的结果就会不一样。这些不同的环境,就是上下文。
-
上下文管理器的应用:上下文管理器通常应用于如下场景:
- 各种类型的文件读写操作
- 各个数据库连接操作
- 多进程,多线程编程环境下
文件读写
-
固定语法:
with open('文件名','读写模式') as 别名: 相关文件操作语句 -
文件读写常用操作(os模块与shutil模块的常用方法) – fp:别名,文件操作对象
- fp.read([size]) #size为读取的长度,以byte为单位
- fp.readline([size]) #读一行,如果定义了size,有可能返回的只是一行的一部分
- fp.readlines([size]) #把文件每一行作为一个list的一个成员,并返回这个list。其实它的内部是通过循环调用readline()来实现的。如果提供size参数,size是表示读取内容的总长,也就是说可能只读到文件的一部分。
- fp.write(str) #把str写到文件中,write()并不会在str后加上一个换行符
- fp.writelines(seq) #把seq的内容全部写到文件中(多行一次性写入)。这个函数也只是忠实地写入,不会在每行后面加上任何东西。
- fp.close() #关闭文件。python会在一个文件不用后自动关闭文件,不过这一功能没有保证,最好还是养成自己关闭的习惯。 如果一个文件在关闭后还对其进行操作会产生ValueError
- fp.flush() #把缓冲区的内容写入硬盘
- fp.fileno() #返回一个长整型的”文件标签“
- fp.isatty() #文件是否是一个终端设备文件(unix系统中的)
- fp.tell() #返回文件操作标记的当前位置,以文件的开头为原点
- fp.next() #返回下一行,并将文件操作标记位移到下一行。把一个file用于for … in file这样的语句时,就是调用next()函数来实现遍历的。
- fp.seek(offset[,whence]) #将文件打操作标记移到offset的位置。这个offset一般是相对于文件的开头来计算的,一般为正数。但如果提供了whence参数就不一定了,whence可以为0表示从头开始计算,1表示以当前位置为原点计算。2表示以文件末尾为原点进行计算。需要注意,如果文件以a或a+的模式打开,每次进行写操作时,文件操作标记会自动返回到文件末尾。
- fp.truncate([size]) #把文件裁成规定的大小,默认的是裁到当前文件操作标记的位置。如果size比文件的大小还要大,依据系统的不同可能是不改变文件,也可能是用0把文件补到相应的大小,也可能是以一些随机的内容加上去。
-
常见文件读写模式及其含义
- w:以写方式打开,
- a:以追加模式打开 (从 EOF 开始, 必要时创建新文件)
- r+:以读写模式打开
- w+:以读写模式打开 (参见 w )
- a+:以读写模式打开 (参见 a )
- rb:以二进制读模式打开
- wb:以二进制写模式打开 (参见 w )
- ab:以二进制追加模式打开 (参见 a )
- rb+:以二进制读写模式打开 (参见 r+ )
- wb+:以二进制读写模式打开 (参见 w+ )
- ab+:以二进制读写模式打开 (参见 a+ )
-
Python中的内置模块:os及shutil模块的常用方法总结
- 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()
- 返回指定目录下的所有文件和目录名:os.listdir()
- 函数用来删除一个文件:os.remove()
- 删除多个目录:os.removedirs(r“c:\python”)
- 检验给出的路径是否是一个文件:os.path.isfile()
- 检验给出的路径是否是一个目录:os.path.isdir()
- 判断是否是绝对路径:os.path.isabs()
- 检验给出的路径是否真地存:os.path.exists()
- 返回一个路径的目录名和文件名:os.path.split() eg os.path.split(‘/home/swaroop/byte/code/poem.txt’) 结果:(‘/home/swaroop/byte/code’, ‘poem.txt’)
- 分离扩展名:os.path.splitext()
- 获取路径名:os.path.dirname()
- 获取文件名:os.path.basename()
- 运行shell命令: os.system()
- 读取和设置环境变量:os.getenv() 与os.putenv()
- 给出当前平台使用的行终止符:os.linesep Windows使用’\r\n’,Linux使用’\n’而Mac使用’\r’
- 指示你正在使用的平台:os.name 对于Windows,它是’nt’,而对于Linux/Unix用户,它是’posix’
- 重命名:os.rename(old, new)
- 创建多级目录:os.makedirs(r“c:\python\test”)
- 创建单个目录:os.mkdir(“test”)
- 获取文件属性:os.stat(file)
- 修改文件权限与时间戳:os.chmod(file)
- 终止当前进程:os.exit()
- 获取文件大小:os.path.getsize(filename)


