Python_迭代器_生成器_详解(16)

一、迭代器

1、迭代器引入

遍历字符串
首先从传统的遍历说起:

for i in "hello":
    print(i)

可以看到下图执行结果,将“hello”逐个打印出来:
在这里插入图片描述
遍历数值

for i in 1234 :
    print(i)

下图执行结果,报错不是一个迭代对象,那么什么是迭代对象了?
iterable 这个 单词就是迭代的意思
在这里插入图片描述

2、迭代对象

  • 1、可以通过循环遍历取出元素的对象就是可迭代对象

  • 2、正统点说,拥有__iter__方法和__next__方法的对象就是迭代器

  • 3、通过 isinstance() 判断一个对象是否是可迭代对象

  • 4、isinstance(obj,Iterable)

我们来分析一组结果,通过上述提到的 isinstance() 方法

from collections import Iterable  # 使用的时候导入

print(isinstance([],list)) # True
print(isinstance([],Iterable)) # True
print(isinstance(123,Iterable))# Fasle

不能发现,通过这个测试,我们可以快速知道是否为可迭代对象,不用再像之前提到的用具体数据去进行测试。

在这里,我们知道了如何确定迭代对象

3、迭代器

3.1 什么是迭代器

__ iter __()方法和 __ next __()方法

  • 其实我们可以通过 dir(str),还有 dir (int)可以查看详细的内置方法,这里就不具体说明了。

  • 帮我我们在迭代遍历时记录访问的位置,以便每次迭代都可以返回下一条数据,拥有__iter__方法和__next__方法的对象就是迭代器

描述 说明
iter () 可以把可迭代对象中的迭代器取出来,内部调用__iter__方法
next() 函数通过迭代器获取下一位置的值,内部调用__next__方法

3.2 迭代器的操作

先看普通的打印

string = "hello"
for i in string:
    print(i)

具有特点:自动打印全部,一个一个取出
在这里插入图片描述


再看迭代器的操作:

string = "hello"
iter1 = iter(string)
print(iter1) # <str_iterator object at 0x0000023C4D039710> 对象
print(next(iter1)) # h
print(next(iter1)) # e
print(next(iter1)) # l
print(next(iter1)) # l
print(next(iter1)) # o

具有特点:非自动,需要一个一个调用打印
注意:print(iter1.__ next __()) 打印也是可以的 ,看个人喜爱
在这里插入图片描述
注意:字符串 “ hello ” 全部打印出,如果我们继续取下一个呢?添加一句
在这里插入图片描述
可以看到报错了,这就是迭代取值得特点,无法超出字符串长度
在这里插入图片描述

3.3 迭代器的应用场景 [ 到底有什么用?]

可能会想,一个一个取,迭代器好麻烦,也可以将获取的迭代对象列表化。
在这里插入图片描述
可以看到,将对象装入到列表,如果调用next()方法,会找不到元素,因为在这里已经全部取出了
在这里插入图片描述

那么迭代器到底有什么用????
节约内存,取得时候在生成数据,python2.7 的时候 range()方法就立刻生成了数据,占用了大 量的内存空间

3.1 数据类型转换 例如 list和元组之间的转换底层就是使用迭代器

str = "hello"
lst1 = list(str)
print(lst1)

tup = ("a","b","c")
lst2 = list(tup)
print(lst2)

在这里插入图片描述

3.2 斐波拉切 【自定义迭代器】

class Fib:
    def __init__(self,n):
        self.n = n # 第几个数
        self.a = 1
        self.b = 1
        self.index = 0 # 记录位置
    def __iter__(self):
        return self # 返回自己 调用自己的__next__()方法
    def __next__(self):
        if self.index < self.n:
            ret = self.a
            self.a,self.b = self.b,self.a+self.b
            self.index += 1
            return ret
        else:
            raise  StopIteration

fib = Fib(5)
iter = iter(fib)
print(list(iter))

在这里插入图片描述
可能看完这个,你还是有点懵,到底啥用啊-QAQ!!
来看这个

3.3 点人数 【自定义迭代器】

"""
迭代器实现计数器
"""
class OnlineCount:

    def __init__(self):
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        return self.index

# 获取可迭代对象
oc = OnlineCount()
# 通过可的迭代对象直接获取下一个数据
print(next(oc))
print(next(oc))
print(next(oc))
print(next(oc))
print(next(oc))

我们来看结果,发现,我们每执行print(next(oc))就打印一个数据,并且还是增加的,如果我们继续,写写不就可以一直加了吗?
所以迭代器,假设我编写了一个操作系统,用户每次点击一次,就记录一次,不是实现了半自动化了嘛~~~~~QAQ

在这里插入图片描述

关键点
1、迭代对象 可以通过dir() isinstance() 来确认
2、迭代对象被唤醒next(ret) 会一个一个取出值,全部取出,值不可再调用

二、生成器

生成器生成也是迭代器的一种

1、举例说明

这里有个简单的函数,想必执行func() 就可以执行了吧,但是你发现了没
有一个 yield 这是一个 生成器的标志性东西,表示 当生成器被调用的时候
返回一个 1

def func():
    print("--func--")
    yield 1
func()

这样直接执行func()是不会有结果的

需要这样执行才会有结果输出

def func():
    print("--func--")
    yield 1

ret = func()
print(ret) # <generator object func at 0x000001E870DFD780> 生成器对象
next(ret)

在这里插入图片描述
我们来看看这个结果,不是说 生成器 yield 1 被调用就会返回跟着的结果吗,我们来
瞅瞅,ret是一个对象,next() 这个方法是,【函数通过迭代器获取下一位置的值,内部调用__next__方法】那么 next(ret) 仅会调用出函数内部及yield前,非生成器标记的执行程序,而我们 yield 1 将值返回了给函数func(),它又是ret,所以需要打印。
并且注释掉,next(ret),因为前面提到了,迭代器会把值抽离出来,那么继续调用不会有任何值了

在这里插入图片描述
可以看到成功的执行所有
在这里插入图片描述

2、生成器 yield 多个之间数据分离

def func():
    print("--func--")
    yield 1
    print("func2")
    yield 2


g = func()

print(next(g))
# print(next(g))

结果图 ,发现只打印第一个yield值及前:
在这里插入图片描述


改变下函数:
def func():
    print("--func--")
    yield 1
    print("func2")
    yield 2


g = func()

print(next(g))
print(next(g))

全部打印出
在这里插入图片描述
生成器之间的yield ,有几个就调用几次,可以全部打印出。






3、生成器应用场景

3.1 斐波拉切 [ 前面是迭代器实现 自行对比]

 # 生成器实现斐波那契数列
def fib(n):
    a,b = 1,1
    index = 0
    print("----1----")
    while index<n:
        print("----2-----")
        yield a
        a,b = b,a+b
        index += 1
        print("----3-----")

g = fib(5)
print(list(iter(g)))

在这里插入图片描述
可以看到执行流程,什么看的懵?QAQ,哪咱们换一种方法

def fib(n):
    a,b = 1,1
    index = 0
    print("----1----")
    while index<n:
        print("----2-----")
        yield a
        a,b = b,a+b
        index += 1
        print("----3-----")

g = fib(5)
# print(list(iter(g)))

print(next(g))
print(next(g))
print(next(g))

我们就看前3个数好了,不然太长了,不好截图,
在这里插入图片描述

3.2 点人数 [ 生成器 ]

def online_counter():
    """无线次数计数器"""
    index = 0
    while True:
        yield index
        index += 1

oc = online_counter() # 获取生成器对象--> 计数功能
print(oc.__next__())
print(next(oc))
print(next(oc))
print(next(oc))

你看,只要继续print(next(oc)) 可以继续输出,无限记数
在这里插入图片描述

4、send()方法

那么用个直观的例子说明问题:

def generator():
    print("a")
    count = yield 1
    print("----->",count)
    print("b")
    yield 2

g = generator()
print(g.__next__())
print(g.__next__())

在这里插入图片描述

我们来看下count = yield 1 ,是否以为count = 1,实际上yield 1优先将值返回给了对象函数,所以为空值

那么有没什么解决办法呢?

send 获取下一个值的效果和 next()基本一致,只是在获取下一个值的时候,给上一 yield 的位 置传递一个数据使用 send 的注意事项:

  • (1).第一次使用生成器的时候 是用 next 获取下一个值
  • (2).最后一个 yield 不能接受外部的值
def generator():
    print("a")
    count = yield 1
    print("----->",count)
    print("b")
    yield 2

g = generator()
print(g.__next__())
print(g.send(10)) # 将 数据传递给挂起位置,并且唤醒函数

在这里插入图片描述

5、yield from

功能 :yield from 循环遍历容器类型

def func():
    yield from "hello"
    yield from range(3)

f = func()
for i in f:
    print(i,end=" ")

其实就是为了简化代码而生的,
在这里插入图片描述

6、生成器表达式

lst = [i for i in range(1,10)]
print(lst)

iter1 = iter(lst)
print(next(iter1)) # 1

g = (i for i in range(1,10))
print(g) # 生成器对象 <generator object <genexpr> at 0x000001FF2E364938>
print(next(g)) # 1
print(next(g)) # 2
print(list(g)) #  [3, 4, 5, 6, 7, 8, 9]

在这里插入图片描述
相比较前面,省略了 iter1 = iter(lst) 直接生成对象

猜你喜欢

转载自blog.csdn.net/weixin_44238683/article/details/106221160