今天就来简单介绍一下Python中的两个高级语法: with上下文管理器 和 yield生成器
文件使用完后必须关闭,因文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的
# 1、以写的方式打开文件
f = open("1.txt", "w")
# 2、写入文件内容
f.write("hello world")
# 3、关闭文件
f.close()
① 由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。
② 为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来解决
# 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、写入文件内容
f.write("hello world")
# 3、关闭文件
f.close()
缺点:try-except-finally语句缺点:代码过于冗长, 不易用,易忘
try:
# 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、读取文件内容
f.write("xxxxx")
except IOError as e:
print("文件操作出错", e)
finally:
# 3、关闭文件
f.close()
上下文管理器with语句:该机制简单、更安全的处理资源和异常
特点:with 语句执行完成后,自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作
实例代码:
# 1、以写的方式打开文件
with open("1.txt", "w") as f:
# 2、读取文件内容
f.write("hello world")
一个类只要实现了__enter__()和__exit__()这个两个方法,通过该类创建的对象我们就称之为上下文管理器
_enter__表示上文方法,需要返回一个操作文件对象
__exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法
"""
(1)写一个文件操作的上下文管理器,并用with语句测试通过
a.要求增加一个read_data函数,读文件操作,只读前10行;
b.提交代码和运行效果
"""
# 1. 自定义上下文管理器类.
class Func:
# 2. 重写 __init__()魔法方法, 初始化: file_name(文件名), file_model(文件模型)
def __init__(self, func_name, func_model):
self.func_name = func_name
self.func_model = func_model
self.f = None # f代表的文件对象.
# 3. 重写 __enter__()函数, 表示: 上文
def __enter__(self):
print('这个是上文, 初始化属性的!')
self.f = open(self.func_name, self.func_model, encoding='utf-8')
return self.f
# 定义read_data函数
def read_data(self):
for i in range(10):
data = self.f.readline()
print(data,end = '')
# 4. 重写 __exit__()函数, 表示: 下文
def __exit__(self, exc_type, exc_val, exc_tb):
print('这个是下文, 释放资源的!')
self.f.close()
# 5. 演示with open()语法, __enter__()返回的是: 文件对象
with Func('./1.txt', 'r') as f:
# print('123')
print(f.read())
根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全
部生成出来,而是使用一个,再生成一个,可以节约大量的内存。
① 生成器推导式 ② yield 关键字
# 创建生成器
# 注意1:括号()代表 这是一个生成器,不是元组
# 注意2:括号()里面写的是数据的生成规则,返回一个对象,
# 对象内不是存的数据,而是产生数据的规则
my_generator = (i * 2 for i in range(5)) # 根据注意2
print(my_generator)
# next获取生成器下一个值
# value = next(my_generator)
# print(value)
# 遍历生成器
for value in my_generator:
print(value)
只要在def函数里面看到有 yield 关键字那么就是生成器
"""
案例: 把给定的歌词文件(100条数据), 按照 8个1批次的形式, 分批包装到 生成其中, 并返回该生成器对象.
"""
# 1. 导包.
import math
# 2. 定义函数, 用于获取生成器, 即: 封装每批次的歌词.
def dataset_loader(batch_size):
"""
自己编写的生成器代码, 用于将文件中的歌词数据, 分批次, 封装到: 生成器中.
:param batch_size: 每批次, 多少条数据.
:return: 无
"""
# 2.1 读歌词文件.
with open('./jaychou_lyrics.txt', 'r', encoding='utf-8') as f:
lines = f.readlines() # 列表, [第1条, 第2条, 第3条......第100条]
# 2.2 计算歌词的总条数.
lyrics_count = len(lines)
# 2.3 计算批次数量, 一共多少批.
# ceil() 天花板数, 比这个数字大的所有整数中, 最小的那个整数, 例如: ceil(12.0) -> 12 ceil(12.1) -> 13, ceil(12.9) -> 13
batch_count = math.ceil(lyrics_count / batch_size) # 13批
# 2.4 根据批次数量, 结合所有的歌词, 封装到 生成器中, 并返回.
for idx in range(batch_count): # idx的值: 0, 1, 2, 3, 4, 5, 6..... 12, 包左不包右.
# 第1批次: 去lines中获取: 0 ~ 8, 包左不包右.
# 第2批次: 去lines中获取: 8 ~ 16, 包左不包右.
# 第3批次: 去lines中获取: 16 ~ 24, 包左不包右.
yield lines[idx * batch_size : idx * batch_size + batch_size]
if __name__ == '__main__':
# 3. 调用函数
my_loader = dataset_loader(8) # 8条为一批.
# 4. 遍历生成器, 获取到每批数据.
# print(next(my_loader)) # 获取第 1 批
# for data in next(my_loader):
# print(data, end='')
#
# print('-' * 31)
# 第2批
# for data in next(my_loader):
# print(data, end='')
# 查看每一批
for data in my_loader:
print(data)