1 迭代器
可迭代对象
遍历也叫迭代。
from collections import Iterable,Iterator
class Classmate(object):
def __init__(self):
self.names = list()
def add(self,name):
self.names.append(name)
def __iter__(self):
#如果想要一个对象称为可迭代对象 即可以使用for那必须实现__iter__方法
return Mates(self)#__iter__方法的返回值是一个迭代器(要有iter和next方法)
#Classmate把自己的引用(self)给Mates,就是地址
class Mates(object):
def __init__(self,obj):
self.obj = obj
self.current_num = 0
def __iter__(self):
pass
def __next__(self):
if self.current_num < len(self.obj.names):
ret = self.obj.names[self.current_num]
self.current_num +=1
return ret
else:
raise StopIteration#抛出这个异常 for循环就自动停了
classmate = Classmate()
classmate.add('123')
classmate.add('234')
classmate.add('345')
#print('calssmate 是否是可迭代对象 ',isinstance(classmate,Iterable))
# classmate_iterator = iter(classmate)
# #iter函数会自动调用classmate的iter方法 iter方法返回的是Mates对象的引用
# print('classmate_iterator 是否是可迭代器 ',isinstance(classmate_iterator,Iterator))
# print(next(classmate_iterator))
for i in classmate:
print(i)
iter方法返回自己
class Classmate(object):
def __init__(self):
self.names = list()
self.current_num = 0
def add(self,name):
self.names.append(name)
def __iter__(self):
#如果想要一个对象称为可迭代对象 即可以使用for那必须实现__iter__方法
return self#__iter__方法的返回值是一个迭代器(要有iter和next方法)
def __next__(self):
if self.current_num < len(self.names):
ret = self.names[self.current_num]
self.current_num +=1
return ret
else:
raise StopIteration#抛出这个异常 for循环就自动停了
classmate = Classmate()
classmate.add('123')
classmate.add('234')
classmate.add('345')
for i in classmate:
print(i)
range函数产生一个列表用于后续处理,但是xrange返回的并不是一个列表,而是一个迭代器,这样做的好处就是节省了空间。xrange配合for循环的遍历效果等同于rnage,数据量很大使用xrange。
斐波那契数列 迭代器
class fin(object):
def __init__(self,all_num):
self.all_num = all_num
self.count = 0
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
if self.count < self.all_num:
ret = self.a
self.a,self.b = self.b,self.a+self.b
self.count+=1
return ret
else:
raise StopIteration
fi = fin(10)
for num in fi:
print(num)
2 生成器(特殊的迭代器)
方法一
nums = [x*2 for x in range(10)]#列表推导式 占用空间
print(nums)
num = (x*2 for x in range(10))#生成器。返回的是生成这个数据的方式
for i in num:
print(i)
方法二
generator很强大,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数实现。
def create_num(all_num):
a, b = 0, 1
cruuent_num = 0
while cruuent_num < all_num:
#print(a)
yield a#如果一个函数中有yield 那么这个就不是函数了而是一个生成器的模版
a, b = b, a+b
cruuent_num += 1
#如果在调用create_num的时候 发现这个函数中有yield 那么此时 不是调用函数 而是创建一个生成器对象
obj = create_num(10)
#next执行生成器
ret = next(obj)
# for num in obj:
# print(num)
可以创建多个生成器对象
obj2 = create_num(10)
通过异常判断生成器已经结束
def create_num(all_num):
a, b = 0, 1
cruuent_num = 0
while cruuent_num < all_num:
#print(a)
yield a#如果一个函数中有yield 那么这个就不是函数了而是一个生成器的模版
a, b = b, a+b
cruuent_num += 1
return 'ok'
#如果在调用create_num的时候 发现这个函数中有yield 那么此时 不是调用函数 而是创建一个生成器对象
obj = create_num(50)
while True:
try:
ret = next(obj)
print(ret)
except Exception as ret:
print(ret.vaule)#想要获得生成器中的返回值 用捕获异常后的ret的vaule值
break
send唤醒生成器
def create_num(all_num):
a, b = 0, 1
cruuent_num = 0
while cruuent_num < all_num:
ret = yield a#ret的值来源于send传递的参数
print('...ret ..',ret)
a, b = b, a+b
cruuent_num += 1
obj = create_num(50)
#obj.send(None) send一般不会放到第一次启动生成器 如果非要这样 那么就传递None
ret = next(obj)
print(ret)
#send里面的数据会当作yield a的结果 然后ret保存这个值
#send的结果是下一次调用yield时 yield后面的值
ret = obj.send('呵呵')#send可以传参数
print(ret)
迭代器减少内存空间 实现循环 生成器可以让函数暂停执行 在通过next send再次执行
3 使用yield实现多任务(协程). 并发
def task_1():
while True:
print('....1....')
yield
def task_2():
while True:
print('....2....')
yield
def main():
t1 = task_1()
t2 = task_2()
while True:
next(t1)
next(t2)#t1 t2交替运行 实现多任务
if __name__ == '__main__':
main()
greenlet gevent完成多任务
from greenlet import greenlet
def task_1():
while True:
print('....1....')
gr2.switch()
def task_2():
while True:
print('....2....')
gr1.switch()
gr1 = greenlet(task_1)#greenlet里面已经封装
gr2 = greenlet(task_2)
#切换到gr1中运行
gr1.switch() #相当于switch替换了next
gevent模块可以自动切换任务
原理就是当一个greenlet遇到IO(指的是input output 输入 输出 比如网络 文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet。等到IO操作完成 再在适当的时候切换回来继续执行。
由于IO操作非常耗时 经常使程序处于等待状态 有了gevent为我们自动切换协程 就保证总有greenlet在运行 而不是等待IO。
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(),i)
#用来模拟耗时操作 注意不是time模块中的sleep
gevent.sleep(0.5)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
g1.join()
g1.join()
g1.join()
给程序打补丁
如果想要用time模块中的sleep 需要gevent的monkey模块. 他会自己替换。
import gevent
from gevent import monkey
import time
monkey.patch_all()
def f(n):
for i in range(n):
print(gevent.getcurrent(),i)
time.sleep(0.5)
gevent.joinall([gevent.spawn(f,5),gevent.spawn(f,5),gevent.spawn(f,5)])
#不用都单独join
进程 线程 协程对比
- 进程是资源分配的单位。
- 线程是操作系统调度的单位。
- 进程切换需要的资源很大,效率很低。
- 协程切换任务资源很小,效率高。
- 多进程 多线程根据cpu核数不一样可能是并行的 但是协程是在一个线程中 所以是并发。