<span>08-迭代器和生成器</span>

一、迭代器和生成器

  迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。

  在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。

1.1、python中的迭代协议

  什么是迭代协议?  迭代协议:一个是 __iter__(也就是可迭代类型),另一个是Iterator(迭代器),迭代器是什么? 迭代器是访问集合内元素的一种方式, 一般用来遍历数据。迭代器和以下标的访问(list[0])方式不一样, 迭代器是不能返回的, 迭代器提供了一种惰性数据的方式。

#list[item]访问的原理是__getitem__
#[] list , __iter__
#Iterable继承的是抽象基类(metaclass=ABCMeta)(__iter__)
#Iterator(迭代器)这个是继承了Iterable,实现的魔法函数有__iter__,__next__
from collections.abc import Iterable, Iterator
a = [1,2]  #list是实现__iter__是可迭代类型,可迭代类型与迭代器(__iter__,__next__)是不一样的
print(isinstance(a, Iterable))#True
print(isinstance(a, Iterator))#False

1.2、什么是迭代器和可迭代对象

from collections.abc import Iterator

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __iter__(self):
        return MyIterator(self.employee) #返回的是迭代器,这样自定义迭代器复用性更强

    # def __getitem__(self, item): #能切片,迭代器是不能切片的
    #     return self.employee[item]


class MyIterator(Iterator):
    def __init__(self, employee_list):
        self.iter_list = employee_list
        self.index = 0 #用迭代器维护这个变量

    def __next__(self):
        #真正返回迭代值的逻辑
        try:
            res= self.iter_list[self.index]
        except IndexError:
            raise StopIteration
        self.index += 1
        return res

if __name__ == "__main__":
    company = Company(["li", "lily", "john"])
    my_itor = iter(company)
    # while True:           #for循环的实现原理
    #     try:
    #         print (next(my_itor))
    #     except StopIteration:
    #         pass

    # 当调用迭代器的时候会这样调用next(my_itor)
    for item in company: #当调用for循环的时候,它会尝试去调用iter(company),首先找的是__iter__,
        print(item)       # 如果没有的话,它就会找__getitem__,

1.3、生成器函数的使用

  生成器函数,函数里只要有yield关键字。

def gen_func():#提交的值都能for循环打印出来
    yield 1
    yield 2
    yield 3

def fib(index):  #1\求斐波拉契只能看见最终结果,看不到过程
    if index <= 2:
        return 1
    else:
        return fib(index-1) + fib(index-2)

def fib2(index): #2\改动之后,将每次产生的值添加到列表中,就可以看到过程中产生的值
    re_list = []
    n,a,b = 0,0,1
    while n<index:
        re_list.append(b)
        a,b = b, a+b
        n += 1
    return re_list

def gen_fib(index):#3\用生成器改动,将每次结果yield出来,
    n,a,b = 0,0,1
    while n<index:
        yield b
        a,b = b, a+b
        n += 1

# 斐波拉契 0 1 1 2 3 5 8
#惰性求值, 延迟求值提供了可能

def func():
    return 1

if __name__ == "__main__":
    #生成器对象, python编译字节码的时候就产生了,
    gen = gen_func()
    for value in gen:
        print (value,end=' ') #1 2 3
    # re = func()
    #将yield提交的值遍历出来
    for data in gen_fib(10):
        print(data,end=' ') #1 1 2 3 5 8 13 21 34 55 

1.4、python是如何实现生成器的

#1.python中函数的工作原理

import inspect
frame = None
def foo():
    bar()
def bar():
    global frame
    frame = inspect.currentframe()

#python.exe会用一个叫做 PyEval_EvalFramEx(c函数)去执行foo函数, 首先会创建一个栈帧(stack frame)
"""
python一切皆对象,栈帧对象,编译成字节码对象
当foo调用子函数 bar, 又会创建一个栈帧
所有的栈帧都是分配在堆内存(栈帧一直在堆内存之中)上,这就决定了栈帧可以独立于调用者存在
"""
# import dis
# print(dis.dis(foo)) #查看运行的字节码过程

foo()
print(frame.f_code.co_name) #bar
caller_frame = frame.f_back
print(caller_frame.f_code.co_name) #foo  #虽然这个函数已经运行完成,我们照样可以去堆内存中找到它的栈帧


def gen_func():
    yield 1
    name = "lishuntao"
    yield 2
    age = 18
    return "HUT"

import dis
gen = gen_func() #返回生成器对象
print (dis.dis(gen))
#人为控制它的执行流程 print(gen.gi_frame.f_lasti) #-1      f_lasti:可以知道进行到哪一个步骤了
print(gen.gi_frame.f_locals) #{}
next(gen)  #进行下一步流程 print(gen.gi_frame.f_lasti)#2
print(gen.gi_frame.f_locals)#{}
next(gen)
print(gen.gi_frame.f_lasti)#12
print(gen.gi_frame.f_locals)#{"name":"lishuntao"}

 

 

1.5、生成器在UserList中的应用

class Company:
    def __getitem__(self, item):
        pass
    def __iter__(self):
        pass
company = Company()
iter(company)  #调用iter的时候,会去对象中寻找__iter__,如果没有的话就去寻找__getitem__

#生成器在list中的应用,如果我们要继承list的话,就继承UserList,因为list是C语言写出来的,UserList是利用python写出来的。
from collections import UserList #UserList中继承的序列Sequence中的__iter__里面就运用生成器实现的数据迭代

1.6、生成器如何读取大文件

当在工作中,应用生成器提取大文件(一行大文件)。

#大文件, 特殊的一行
def myreadlines(f, newline):
  buf = ""     #设置空的缓存区
  while True:
    #第一次进入buf为空,直接跳到下面读取文件
    while newline in buf:
      pos = buf.index(newline)
      yield buf[:pos]  #提交第一行
      buf = buf[pos + len(newline):] #将提交出去的舍弃掉
    chunk = f.read(4096)

    if not chunk:
      #说明已经读到了文件结尾,然后将buf提交出去
      yield buf
      break
    buf += chunk

with open("input.txt") as f:
    for line in myreadlines(f, "{|}"):
        print (line)
"""
Prior to beginning tutoring sessions
, I ask new students to fill
 out a brief self-assessment
 where they rate their
 understanding of various Python concepts. Some topics ("control flow with if/else" or "defining and using functions") are understood by a majority of students before ever beginning tutoring. There are a handful of topics, however, that almost all
 students report having no knowledge or very limited understanding of. Of these
"""
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务