有的对象可以用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 selfdef __next__(self):line = self.file.readline()if line:return lineelse: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 lineline = 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 iprint('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 lineelse:continue
line_gen = line_generator(filepath)
for line in line_gen:process_line(line)
可想而知,这个内存也极小
好处: 惰性计算 很好节省内存资源 生成可以没有尽头
斐波那契为例子:
def fib_generator():cur,nxt = 0,1while True:yield curcur,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*xmulti_gen = multiplcation_generator(2)
print(next(multi_gen)) #2
print(next(multi_gen)) #4
print(next(multi_gen)) #6
print(next(multi_gen)) #8