8.迭代器和生成器

1. python的迭代协议

什么是迭代协议?

凡是 能用 for循环的,背后都是迭代协议。 可迭代类型内置魔法方法__iter__

迭代器是什么?

迭代器是访问集合内元素的一种方式, 一般用来遍历数据
迭代器和以下标访问方式不一样,迭代器是不能返回的,迭代器提供了一种惰性的访问方式

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关键字的函数
以生成斐波那契函数为例,说明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)


猜你喜欢

转载自blog.csdn.net/weixin_41207499/article/details/80560448