迭代器
迭代器是访问集合元素的一种方式。
迭代器保存的是生成数据的代码,并不是代码生成的数据结果。
迭代器实现了循环,减少内存空间的使用,可以规定数据的生成方式。
1.到底什么是迭代器,判断是否是一个迭代器,跟着栗子来....
for temp in temp_obj:
pass
判断步骤:
- 判断 temp_obj 对象是否可以迭代。(可迭代对象移动包含__iter__方法)
- 在第一步条件成立的情况下,调用其__iter__方法,会得到__iter__方法 return 的返回值。(可迭代的对象)
- __iter__方法返回的是一个迭代器
2.在开发过程当中,创建对象后添加很多数据,能否通过for取出这些数据
- 创建的这个对象的类里边必须包含一个__iter__方法。
- Iter方法必须返回一个迭代器的对象。
- 迭代器:必须包含__iter__方法和__next__方法。一大特点可以取里边的值。next工作原理就是通过重复调用next()方法,直到捕获一个异常。
3.使用迭代器和不使用迭代器的区别:
当你需要一些数据的时候可以这样做:
- 可以找列表将数据预先存起来。占用空间大
- 什么时候用什么时候生成。只存储生成方式。占用空间小。
4.并不是只有for循环能接受可迭代对象,可以通过列表或元组去取值:
A=[11,22,33,44,55]
li=list(fibIterator(A))
Print(li)
#先生成一个空列表,再去调用list里的迭代器,
#因为A是一个迭代对象,通过iter函数就会找到迭代器,通过next去取值。
li=tuple(fibIterator(A))
Print(li)
5.判断其是否是一个可迭代的对象
from collections.abc import Iterable
from collections.abc import Iterator
import time
# print(isinstance([11,22,33],Iterable))
# print(isinstance("1111",Iterable))
# print(isinstance(111,Iterable))
迭代器案例:
class Fibonacci(object):
def __init__(self,number):
self.number=number
self.count=0
self.a=0
self.b=1
def __iter__(self):
return self
def __next__(self):
if self.count<self.number:
ret=self.a
self.a,self.b=self.b,self.a+self.b
self.count+=1
return ret
else:
raise StopIteration
feibo=Fibonacci(10)
for i in feibo:
print(i)
这里说一下跳出迭代器next()方法,可以抛出StopIteration异常就无法继续返回下一个值了,否则最后会一直调用返回None。
生成器
生成器是一种特殊的迭代器。在 Python 中,使用了 yield 的函数被称为生成器(generator)。
1.创建生成器的两种方式:
-
将列表推导式的中括号换为圆括号(
列表推导式:存储数据占用内存大
)
-
yield创建生成器 (生成器:只存放数据的存储方式,占用内存小)
2.启动生成器的方式:除了next,还有send。
def createnum(all_num):
print("-----1-----")
a,b=0,1
num=0
while num<all_num:
# print(a)
print("-----2-------")
yield a#yield会将函数暂停,下次从下一个再次执行。
#运行到yield时会把yield后a传递给number
#在下次for循环会直接从yield处开始执行,并不会从头开始执行。
print("-----3-----")
a,b=b,a+b
num+=1
print("------4-------")
#如果在调用createnum()的时候,如果有yield,就不是调用函数,就是创建了一个
#生成器对象。
obj=createnum(10)
#生成器是一种特殊的迭代器。
# for number in obj:
# #直到没有for循环-->结束。
# print(number)
while True:
try:
ret=next(object)
print(ret)
except Exception as ret:
break
send方式:
可以传参数,send传送的是yield a表达式的最后的结果
在生成器在生成数的时候,send可用于传送新的参考值,接收可以yield前加参数接收。
send不要放到next前。启动第一次一般用next.
def createnum(all_num):
a,b=0,1
num=0
while num<all_num:
ret=yield a
print(">>>>ret", ret)
a,b=b,a+b
num+=1
obj=createnum(10)
ret=next(obj)#不能传参数
print(ret)
ret=obj.send('xxxxxx')
print(ret)
3.使用yield完成多任务(并发)
进程之间切换多任务,占用资源会很大。而且创建进程(从硬盘上加载代码、申请内存空间)、释放进程是个很复杂的过程,浪费大量的时间。
进程没有线程效率高,但协程比线程运行效率更高。协程调用一个任务就像调用一个函数这样简单,切换耗费的资源最少。进程消耗资源最多,线程次之。
参考代码:
import time
def task_1():
while True:
print("-----1-----")
time.sleep(1)
yield
def task_2():
while True:
print("-----2-----")
time.sleep(1)
yield
#两个函数一起执行
if __name__ == '__main__':
t1=task_1()
t2=task_2()
while True:
next(t1)
next(t2)
只需要在要执行的函数加yield即可
3.1使用greenlet来实现多任务
pip3 install greenlet
参考代码:
from greenlet import greenlet
import time
def test1():
while True:
print("----A----")
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print("----B----")
gr1.switch()
time.sleep(0.5)
gr1=greenlet(test1)#返回greelet对象
gr2=greenlet(test2)
#切换到gr1中运行
gr1.switch() #greenlet的switch方法可以实现函数的切换
3.2 gevent的网络异步并发库
pip install gevent
用多协程实现多任务的方式是:单线程利用了等待耗时的时间去执行其他事情。线程结束,协程也就结束了
import gevent
def f1(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(0.5)
#第一个参数为指定任务去去哪执行
#第二个传递的所需参数,有写无不写
print("------1------")
g1=gevent.spawn(f1,5)#创建greenlet对象
print("------2------")
g2=gevent.spawn(f2,5)
print("------3------")
g3=gevent.spawn(f3,5)
print("------4------")
g1.join()#等待g1执行完
g2.join()
g3.join()
这里的延时和堵塞需要都要换成gevent里面的方法。有简单的方法实现:给程序打补丁,使用
import gevent
from gevent import monkey
monkey.patch_all()
使用
monkey.patch_all()代码不用改,会自动改成gevent代码。
monkey将当前整个代码,读到某个地方去,然后将当前代码的所有耗时操作检查一遍,全有换成gevent中的方法,这样就可以按照你的方法写代码。
import gevent
import time
from gevent import monkey
monkey.patch_all()
def f1(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(0.5)
def f2(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(0.5)
def f3(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(0.5)
#第一个参数为指定任务去去哪执行
#第二个传递的所需参数,有写无不写
print("------1------")
g1=gevent.spawn(f1,5)#创建greenlet对象
print("------2------")
g2=gevent.spawn(f2,5)
print("------3------")
g3=gevent.spawn(f3,5)
print("------4------")
gevent.joinall([
gevent.spawn(f1,5),
gevent.spawn(f2,5),
gevent.spawn(f3,5)
])
这里可以用joinall()方法中放列表来实现等前一个执行完再执行自己。如果分开写:
g1.join()#等待g1执行完
g2.join()
g3.join()
如果数量太大,写出来太麻烦