python编程语法基础笔记(4.8)(迭代器与生成器、文件操作)
今天系统学习了 Python 中迭代器、生成器以及文件操作的核心知识点,这些内容是 Python 进阶的重要基础,尤其是迭代器和生成器的惰性计算特性,在处理大数据场景时能显著优化性能,下面对所学内容进行详细梳理。
一、迭代器(Iterator)
1. 核心概念
可迭代对象:能被for循环遍历的对象,本质是实现了__iter__方法。
检查是否为可迭代对象:hasattr(对象, '__iter__'),返回True则是可迭代对象。
常见可迭代对象:列表、字符串、元组、自定义类实例(手动实现__iter__)。
迭代器:遵循「迭代器协议」的对象,需同时满足两个条件:
能被iter()接收(实现__iter__方法,且返回自身);
能被next()逐步取值(实现__next__方法)。
迭代器特性:
惰性计算:按需生成值,不一次性加载所有数据,降低内存占用;
一次性:状态只能前进,遍历后会被消耗,无法重复使用;
调用next()取值,无数据时抛出StopIteration异常。
2. for 循环的底层逻辑
# 以遍历列表为例
names = ['张三','李四','王五']
it = iter(names) # 1. 获取可迭代对象的迭代器
while True:
try:
print(next(it)) # 2. 调用next()取值
except StopIteration: # 3. 捕获异常,终止循环
break
3. 自定义迭代器
自定义迭代器有两种实现方式,核心是实现__iter__和__next__方法:
方式 1:类自身作为迭代器(迭代器 = 可迭代对象)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
self.__index = 0
self.__arr = [name, age]
def __iter__(self):
return self # 返回自身作为迭代器
def __next__(self):
if self.__index < len(self.__arr):
self.__index += 1
return self.__arr[self.__index - 1]
else:
raise StopIteration # 无数据时抛异常
p1 = Person('张三',18)
for i in p1:
print(i) # 遍历输出:张三 18
方式 2:分离可迭代对象和迭代器(更符合规范)
class Person: # 可迭代对象
def __init__(self, name, age):
self.name = name
self.age = age
self.hobby = []
def __iter__(self):
return PersonIterator(self) # 返回独立的迭代器对象
class PersonIterator: # 迭代器类
def __init__(self, p):
self.p = p
self.index = 0
self.arr = [p.name, p.age, p.hobby]
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.arr):
raise StopIteration
value = self.arr[self.index]
self.index += 1
return value
4. 迭代器的优势(惰性计算)
以斐波那契数列为例,对比列表生成和迭代器生成的内存占用:
import tracemalloc
# 迭代器实现(惰性计算,内存占用极低)
class Fib:
def __init__(self,total):
self.one = 1
self.two = 1
self.total = total
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= self.total:
raise StopIteration
if self.index < 2:
self.index += 1
return self.one
self.three = self.one + self.two
self.one = self.two
self.two = self.three
self.index += 1
return self.three
# 列表实现(一次性生成所有数据,内存占用高)
def fib(n):
num = [1,1]
for i in range(n):
num.append(num[i]+num[i+1])
return num[:n]
# 内存对比
tracemalloc.start()
f1 = Fib(80)
m1 = tracemalloc.get_traced_memory()[1]
print(f'迭代器内存占用:{m1/1024/1024}MB') # 接近0MB
tracemalloc.start()
f2 = fib(80)
m1 = tracemalloc.get_traced_memory()[1]
print(f'列表内存占用:{m1/1024/1024}MB') # 远大于迭代器
二、生成器(Generator)
生成器是 Python 内置的「简化版迭代器」,无需手动实现__iter__和__next__,核心是yield关键字。
1. 生成器函数的定义
函数体中出现yield关键字,该函数即为生成器函数;
调用生成器函数时,不会立即执行函数体,而是返回一个生成器对象。
def test():
print('函数开始执行')
yield 'one' # 暂停并返回值
a = 200
print(a)
yield 'two'
res = test() # 生成器对象,函数未执行
print(type(res)) # <class 'generator'>
2. 生成器的执行逻辑
调用next(生成器)或生成器.__next__(),函数开始执行,遇到yield暂停,记录暂停位置;
再次调用next(),从暂停位置继续执行,直到再次遇到yield;
遇到return时,抛出StopIteration异常,return后的内容作为异常信息返回;
yield后的值,会作为本次next()的返回值。
res = test()
print(next(res)) # 执行到第一个yield,输出:函数开始执行 → 返回one
print(next(res)) # 从暂停位置继续,输出:200 → 返回two
# 第三次next()会抛StopIteration(无更多yield)
3. 生成器的常用操作
(1)for 循环遍历生成器
生成器是可迭代对象,可直接用for循环
def car():
for i in range(3):
yield f'第{i+1}次打印'
a = car()
for i in a:
print(i) # 输出:第1次打印 第2次打印 第3次打印
(2)yield from:批量 yield 可迭代对象的元素
def demo():
nums = [1, 2, 3]
yield from nums # 等价于:for num in nums: yield num
a = demo()
print(next(a)) # 1
print(next(a)) # 2
(3)send ():双向通信(给 yield 传值)
next()只能取值,send()可同时「传值 + 取值」,注意:
第一次启动生成器,需用next()或send(None);
a = yield '返回值':右侧值返回给外部,左侧接收外部send的参数。
def demo():
print('函数开始执行')
a = yield '我是第一次返回值' # 接收send的参数
print(a,'@@@')
b = yield '我是第二次返回值'
print(b,'$$$')
return 'hello'
d = demo()
d1 = next(d) # 启动生成器,返回:我是第一次返回值
d2 = d.send('55') # 给上一个yield传值55,返回:我是第二次返回值
try:
d3 = d.send(777) # 传值777,触发return,抛StopIteration
except StopIteration as e:
print(e) # 输出:hello(return的内容)
三、文件操作
1. 文件分类
| 类型 | 编码方式 | 示例文件 | 读取 / 写入要求 |
|---|---|---|---|
| 纯文本文件 | 遵循字符编码(如 utf-8) | .txt、.py、.md | 需指定编码,读取为字符串 |
| 二进制文件 | 无字符编码,按格式存储 | .mp3、.png、.doc | 以二进制模式(b)操作,无需编码 |
2. 文件路径
绝对路径:从文件系统根目录开始,如D:\Apython\SPT2603\4.文件操作\1.文件分类.py;
相对路径:以当前工作目录为参照,如a.txt(当前目录下)、../b.txt(上级目录)。
3. 文件操作流程
核心步骤:创建文件对象 → 操作文件 → 关闭文件(推荐用with自动关闭)。
(1)读取文件
文件打开模式:rt(文本读取,默认)、rb(二进制读取)。
| 方法 | 作用 |
|---|---|
read(size) | 无参数:读取全部内容;有参数:读取指定字符数(文本)/ 字节数(二进制) |
readline() | 读取一行,size参数仅限制当前行的读取长度 |
readlines() | 无参数:返回所有行的列表;有参数:读取包含指定字符数的所有行 |
for line in f | 逐行遍历文件(推荐,惰性读取,低内存) |
# 逐行读取(最优方式)
with open('a.txt', mode='rt', encoding='utf-8') as f:
for line in f:
print(line, end='') # end=''避免重复换行
(2)写入文件
文件打开模式:
wt:清空后写入,文件不存在则创建;
xt:排他性创建,文件存在则报错;
at:追加写入,文件不存在则创建;
rt+/wt+:读写模式(需注意指针位置)。
# 追加写入
with open('a.txt', mode='at', encoding='utf-8') as f:
f.write('追加的内容')
# 强制刷盘(跳过缓冲区,立即写入文件)
f.write('内容')
f.flush()
(3)文件指针(seek ())
调整文件读写指针位置,格式:f.seek(offset, whence):
offset:偏移量(字节数);
whence:参考点(0 = 文件开头,1 = 当前位置,2 = 文件末尾);
文本模式(t):whence=1/2时,offset只能为 0;二进制模式(b)无限制。
with open('a.txt', mode='rt+', encoding='utf-8') as f:
f.seek(0, 2) # 指针移到文件末尾
f.write('在末尾追加内容')
四、核心总结
迭代器:手动实现__iter__+__next__,惰性计算,适合大数据场景,是可迭代对象的「取值工具」;
生成器:内置迭代器,通过yield简化迭代器实现,支持send()双向通信,代码更简洁;
文件操作:区分文本 / 二进制文件,优先用with自动关闭文件,读取大文件推荐逐行遍历,写入注意模式选择和缓冲区刷盘;
迭代器和生成器的核心价值:惰性计算,降低内存占用,处理无限序列 / 超大文件时优势显著。
更多推荐


所有评论(0)