文章目录
一、迭代器
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)
直接生成对象