1. 迭代器
1.1 可迭代对象
判断xxx_obj是否可以迭代
在第1步成立的前提下,调用 iter 函数得到 xxx_obj 对象的 __iter__ 方法的返回值
__iter__ 方法的返回值是一个迭代器
如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法
__iter__ 中必须返回对象的引用【要这个对象有__iter__和__next__方法, 实际上取的__next__的返回值】
迭代器结束,需要抛出一个 StopIteration 异常。
from collections import Iterable from collections import Iterator import time class Classmate(object): def __init__(self): self.names = list() def add(self, name): self.names.append(name) def __iter__(self): # 如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法 return ClassIterator(self) # 必须返回 class ClassIterator(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 classmate = Classmate() classmate.add("王1") classmate.add("李2") classmate.add("张3") # print("classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable)) # classmate_iterator = iter(classmate) # print("classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator)) # iter # print(next(classmate_iterator)) # for name in classmate: print(name) time.sleep(1)
王1
李2
张3
1.2 调用自己的__next__方法
from collections import Iterable from collections import Iterator import time 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 # 调用返回对象的__next__方法(这里调用自己的__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 # 停止迭代 classmate = Classmate() classmate.add("王1") classmate.add("李2") classmate.add("张3") # print("classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable)) # classmate_iterator = iter(classmate) # print("classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator)) # iter # print(next(classmate_iterator)) # for name in classmate: print(name) time.sleep(1)
王1
李2
张3
生成斐波那契数列
class Fibnacci(object): def __init__(self, all_num): self.all_nums = all_num self.current_num = 0 self.a = 0 self.b = 1 def __iter__(self): return self def __next__(self): if self.current_num < self.all_nums: ret = self.a self.a, self.b = self.b, self.a+self.b self.current_num += 1 return ret else: raise StopIteration fibo = Fibnacci(10) for num in fibo: print(num)
2. 生成器--一种特殊迭代器
2.1 创建生成器1:()
2.2 创建生成器2:yield
def create_num(all_num): # a = 1 # b = 1 a, b = 0, 1 current_num = 0 while current_num < all_num: yield a # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板 a, b = b, a + b current_num += 1 if __name__ == '__main__': # 如果在调用create_num的时候,发现这个函数有yield,此时不是调用函数,而是创建一个生成器对象 obj = create_num(10) for num in obj: print(num)
注意:yield的工作流程
2.3 两个生成器之间没有影响
2.4 通过异常判断生成器已结束
2.5 生成器获得return的值
2.6 send使用--启动生成器
def create_num(all_num): a, b = 0, 1 current_num = 0 while current_num < all_num: res = yield a print(">>>>ret>>>>", res) a, b = b, a + b current_num += 1 if __name__ == '__main__': obj = create_num(4) # obj.send(None) # send一般不会放到第一次启动生成器,如果非要如此,传递None ret = next(obj) print(ret) ret = obj.send("hhhhh") print(ret) # send里面的数据,会传递给第5行,当作yield a的结果,然后res保存这个结果.. # send的结果是下一次调用yield时,yield后面的值 ret = obj.send(None) print(ret) ret = obj.send(None) print(ret)
注意:send不要放第一次
2.7 yield和return区别
yield可以暂停函数执行,且下一次执行时候恢复
2.8 迭代器和生成器作用
- 迭代器: 减少内存空间, 能实现循环
- 生成器: 能让一个函数看上去能暂停执行
- 都是保证生成数据代码, 不是保存结果
生成器(yield): 实现多任务 !
3. 多任务-协程(yield执行)
进程占资源最多, 其次线程, 协程占资源最少!
#!/bin/python3 # -*- coding=utf-8 -*- import time def task_1(): while True: print("------1-------") time.sleep(0.1) yield def task_2(): while True: print("------2------") time.sleep(0.2) yield def main(): t1 = task_1() t2 = task_2() while True: next(t1) next(t2) if __name__ == "__main__": main()
并行: 有两个任务, 但是有四个CPU的核, 一个任务占一个核, 每个都在做
并发: 有很多任务, 但是只有两个核, 所以 交替执行
4. greenlet实现多任务(核心还是yield)
#!/bin/python3 # -*- encoding=utf-8 -*- 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) gr2 = greenlet(test2) # 切换到gr1中执行 gr1.switch()
5. gevent实现协程(更强大,常用)
#!/bin/python3 # -*-encoding=utf-8-*- import gevent import time 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) print("----2-----") g2 = gevent.spawn(f2, 5) print("----3-----") g3 = gevent.spawn(f3, 5) print("----4-----") g1.join() g2.join() g3.join()
gevent遇到延时操作就切换, 利用了等待耗时的操作, 去做其他的事情
如下: 加入monkey.patch_all()则无须将 time.sleep()改成 gevent.sleep()
#!/bin/python3 # -*-encoding=utf-8-*- import gevent from gevent import monkey import time monkey.patch_all() def f1(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) def f2(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) def f3(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) print("----1-----") g1 = gevent.spawn(f1, 5) print("----2-----") g2 = gevent.spawn(f2, 5) print("----3-----") g3 = gevent.spawn(f3, 5) print("----4-----") g1.join() g2.join() g3.join()
如下: 将需要join的代码, 写成列表, 简洁
#!/bin/python3 # -*-encoding=utf-8-*- import gevent from gevent import monkey import time monkey.patch_all() def f1(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) def f2(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) def f3(n): for i in range(n): print(gevent.getcurrent(), i) time.sleep(0.5) # gevent.sleep(0.5) gevent.joinall([ gevent.spawn(f1, 5), gevent.spawn(f2, 5), gevent.spawn(f3, 5) ])
6. 并发下载器
#!/bin/python3 #-*- encoding=utf-8 -*- import gevent import urllib.request from gevent import monkey monkey.patch_all() def downloader(img_name, img_url): req = urllib.request.urlopen(img_url) img_content = req.read() with open("./img/"+ img_name, "wb") as f: f.write(img_content) def main(): gevent.joinall([ gevent.spawn(downloader, "1.jpg", 'https://rpic.douyucdn.cn/asrpic/190417/5440020_3968619_65b10_2_2142.jpg'), gevent.spawn(downloader, '2.png', "https://rpic.douyucdn.cn/asrpic/190417/594613_2143.png") ]) if __name__=="__main__": main()
7. 进程/线程/协程对比
-
进程: 耗费资源最多, 进程里一定有一个线程, 默认线程称为主线程。进程是资源分配的单位。(最稳定, 耗费资源最多)
-
线程: 线程是操作系统调度的单位. 线程切换需要的资源一般, 效率一般 (不考虑GIL情况)
-
协程: 协程切换任务资源很小, 效率高;
-
特点: 在等待某个资源到来 期间, 去执行其他代码....多线程里有很多网络堵塞, 推荐先用协程 !
-
- 多进程、多线程根据cpu核数不一样 可能是并行的, 但是协程是在一个线程中, 所以是并发的!