1. python的迭代协议
什么是迭代协议?
凡是 能用 for循环的,背后都是迭代协议。 可迭代类型内置魔法方法__iter__
迭代器是什么?
迭代器是访问集合内元素的一种方式, 一般用来遍历数据<br> 迭代器和以下标访问方式不一样,迭代器是不能返回的,迭代器提供了一种惰性的访问方式
list是可迭代的,但不是一个迭代器
# Iterable 可迭代 中有__iter__
# 而Iterator迭代器 继承类Iterable 并有魔法方法__next__
>>> from collections.abc import Iterable, Iterator
>>> a = [1, 2]
>>> print(isinstance(a, Iterable)) # a是可迭代的
True
>>> print(isinstance(a, Iterator)) # a不是迭代器
False
# 把a变为迭代器
>>> iterator = iter(a)
>>> print(isinstance(iterator, Iterator))
True
python源码中 我们可以 参考list的实现原理。(list是c实现的,源码不可看。我们可以看 collections.Userlist, 这是用python语言重写的list)
2. 迭代器和可迭代对象
-
迭代器的特点是 只有在需要的时候 才会取值。
现在让我自己写一个迭代器
>>> from collections.abc import Iterator
# company 是一个迭代器, 但是 它的__next__魔法函数 并不在自己内部写。
# 把__next__写在MyIterator中,index索引就不用在Company内部做了。 更符合编码规范
>>> class Company:
>>> def __init__(self, employee_list):
>>> self.employee = employee_list
>>> def __iter__(self): # for in 结构时 调用
>>> return MyIterator(self.employee)
>>> def __getitem__(self, item): # company[1] 角标索引时 调用
>>> return self.employee[item]
>>> class MyIterator(Iterator): # 继承Iterator 不需要写 __iter__
>>> def __init__(self, employee_list):
>>> self.iter_list = employee_list
>>> self.index = 0
# 生成器的魔法方法
>>> def __next__(self): # 真正返回迭代值的逻辑
>>> try:
>>> word = self.iter_list[self.index]
>>> except IndexError:
>>> raise StopIteration
>>> self.index += 1
>>> return word
>>> if __name__ == '__main__':
>>> company = Company(['tom', 'bob', 'jane'])
>>> print(company[1])
bob
>>> for item in company:
>>> print(item)
tom
bob
jane
3. 生成器函数的使用
生成器函数: 有yield关键字的函数<br> 以生成斐波那契函数为例,说明yield的使用
>>> def gen_fib(index):
>>> n, a, b = 0, 0, 1
>>> while n < index:
>>> yield b
>>> a, b = b, a+b
>>> n += 1
>>> if __name__ == '__main__':
>>> for fib in gen_fib(6):
>>> print(fib)
1
1
2
3
5
8
4. 生成器的应用:读取大文件
情景:
目的:500G 的大文件, 一行一行读取, 再写入数据库.
困难:但大文件的所有行 都是写在了同一行上,行与行 之间以'{|}' 隔开,常规方法不可用了。
我们需要针对这种情况 用yield实现读取大文件
代码答案:
def myreadlines(f, newline): # f文件指针, newline行与行的分隔符
buf = "" # 缓存
while True:
while newline in buf: # buf中有分隔符,就不停循环
pos = buf.index(newline) # 找到缓存中 分隔符的位置
yield buf[:pos] # 分隔符前的数据 yield出去
buf = buf[pos + len(newline):] # buf更新, yield过的,丢弃
chunk = f.read(4096) # 每次读入4096个字符
if not chunk:
yield buf
break
buf += chunk
with open("sourcefile.txt") as f:
for line in myreadlines(f, "{|}"):
print(line)