从迭代器到生成器:小内存也能处理大数据

有的对象可以用for循环比如字符串和列表,有的对象不可以比如整数

my_str = '123'
for s in my_str;
	print(s)

my_lst = [1,2,3]
for i in my_lst:
	print(i)

my_int = 123
for n in my_int:
	print(n) # 报错

python中能够使用for循环迭代的对象叫可迭代对象也叫iterables
iterables包含__iter__方法,我们开头通过hasattr这个函数查看某一个对象是否有某个属性或者方法

print(hasattr(my_str,'__iter__')) #True
print(hasattr(my_lst,'__iter__')) #True
print(hasattr(my_int,'__iter__')) #False

像__iter__这样前后有两个下划线的方法叫特殊方法,也叫魔法方法,一般用来控制对象要怎么回应Python的内置行为,那么__iter__这个方法可以做什么呢? 为什么有了它 就可以在for循环中进行迭代?这跟for循环的原理有关

在这里插入图片描述

it = iter(my_str) # my_str.__iter__() 这样是等价的
while True:
	try:
		print(next(it))
	except StopIteration:
		break

什么是迭代器,先给出迭代器的定义:
迭代器需要包含两个魔法方法: __iter__,__next__ 可以说只要包含了这两个方法的对象就是迭代器,这两个方法用来做什么?iter()就会返回一个迭代器
迭代器的iter就会返回迭代器本身,对于next每次调用都要获取下一个迭代器的值,特点是一直往下走,不会回头,直到没有下一个值时raise 一个 stopiteration异常,此时迭代器就用完了,我们就不能再从这个迭代器中获取值了,如果我们还想用,需要对可迭代对象重新调用iter,创建一个新的迭代器.

实际例子: 假设有一个伪造了10万行数据的logfile.log文件

def process_line(line):
	pass # 这里不是重点
import tracemalloc # 引入内置模块跟踪程序内存使用情况
tracemallic.start()
filepath = './logfile.log'
with open(filepath,'r') as f:
	lines = f.readlines() #全部读取以列表的形式存在变量中
for line in lines:
	process_line(line)
# 将文件内容都存在于内存中,毫无疑问内存占用会相当大
current,peak = tracemalloc.get_traced_memory()
print(f"Cuurent memory usage:{current / 1024**2} MB")
print(f'peak memory usage:{peak  / 1024**2 }MB') #10MB
tracemalloc.stop() 

可以一行行处理:

class LineIterator:
	def __init__(self,filepath):
		self.file = open(filepath,'r')
	def __iter__(self):
		return self
	def __next__(self):
		line = self.file.readline()
		if line:
			return line
		else:
			self.file.close()
			raise StopIteration
line_iter = LineIterator(filepath)
for line in line_iter:
	process_line(line) 
# 结果不到0.1MB 因为我们并没有把文件内容加载到内存中

理解了迭代器的内部构造之后
我不想处理所有的行,我只想处理操作类型是create的行
在这里插入图片描述

def __next__(self):
	line = self.file.readline()
	while line:
		if line.split('|')[2].strip() == 'Create':
			return line
		line = self.file.readline()
	self.file.close()
	raise StopIteration

从这里我们就可以看出迭代器最重要的部分是next方法,init和iter更像是一个累赘,如果你有这种感觉,那么有一个好消息Python有一个东西叫做生成器可以理解为迭代器的简单实现,生成器有两种写法,分别是生成器函数和生成器表达式,在这里只会提及生成器函数

这是一个简单的生成器函数,这个函数在调用时不会返回具体的值而是返回一个生成器.
生成器函数会包含yield的关键字,通过这个关键字,生成器会自动产生__iter____next__方法,它的运行规则是,在yield的这行产生一个值然后退出函数,下次进来时又从yield处继续,这里有两行打印函数方便我们观察,与迭代器相同我们可以通过next和for查看值

def generator(n):
	for i in range(n):
		print('before yield')
		yield i
		print('after yield')

gen = generator(3)
print(next(gen))
print('___')
for i in gen:
	print(i)

before yield
0


after yield
before yield
1
after yield
before yield
2
after yield

我们可以更改上面那个文件的代码:

def line_generator(filepath):
	with open(filepath,'r') as file:
		for line in file:
			if line.split('|')[2].strip() == 'Create':
				yield line
			else:
				continue
line_gen = line_generator(filepath)
for line in line_gen:
	process_line(line)

可想而知,这个内存也极小
好处: 惰性计算 很好节省内存资源 生成可以没有尽头
斐波那契为例子:

def fib_generator():
	cur,nxt = 0,1
	while True:
		yield cur
		cur,next = nxt,cur+nxt
fib_gen = fib_generator()
for _ in range(10):
	print(next(fib_gen))
# 

一道简单的题目:

# 生成器函数小练习
def multiplcation_generator(x):
    for i in range(1,10):
        yield i*x


multi_gen = multiplcation_generator(2)
print(next(multi_gen)) #2
print(next(multi_gen)) #4
print(next(multi_gen)) #6
print(next(multi_gen)) #8
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值