1、迭代
1.1 迭代的概念
应用for循环遍历取值的过程叫做迭代,比方:应用for循环遍历列表获取值的过程
for value in [2, 3, 4]: print(value)
1.2 可迭代对象
规范概念:在类外面定义
__iter__
办法,并应用该类创立的对象就是可迭代对象简略记忆:应用for循环遍历取值的对象叫做可迭代对象, 比方:列表、元组、字典、汇合、range、字符串
1.3 判断对象是否是可迭代对象
from collections import Iterableresult = isinstance((3, 5), Iterable)print("元组是否是可迭代对象:", result)result = isinstance([3, 5], Iterable)print("列表是否是可迭代对象:", result)result = isinstance({"name": "张三"}, Iterable)print("字典是否是可迭代对象:", result)result = isinstance("hello", Iterable)print("字符串是否是可迭代对象:", result)result = isinstance({3, 5}, Iterable)print("汇合是否是可迭代对象:", result)result = isinstance(range(5), Iterable)print("range是否是可迭代对象:", result)result = isinstance(5, Iterable)print("整数是否是可迭代对象:", result)result = isinstance(5, int)print("整数是否是int类型对象:", result)class Student(object): passstu = Student()result = isinstance(stu, Iterable)print("stu是否是可迭代对象:", result)result = isinstance(stu, Student)print("stu是否是Student类型的对象:", result)
1.4 自定义可迭代对象
在类中实现__iter__
办法
自定义可迭代类型代码
from collections import Iterableclass MyList(object): def __init__(self): self.my_list = list() def append_item(self, item): self.my_list.append(item) def __iter__(self): passmy_list = MyList()my_list.append_item(1)my_list.append_item(2)result = isinstance(my_list, Iterable)print(result)for value in my_list: print(value)
执行后果:
Traceback (most recent call last):True File "/Users/hbin/Desktop/untitled/aa.py", line 24, in <module> for value in my_list:TypeError: iter() returned non-iterator of type 'NoneType'
通过执行后果能够看进去,遍历可迭代对象顺次获取数据须要迭代器
总结
在类外面提供一个__iter__
创立的对象是可迭代对象,可迭代对象是须要迭代器实现数据迭代的
2、迭代器
2.1 自定义迭代器对象
自定义迭代器对象: 在类外面定义__iter__
和__next__
办法创立的对象就是迭代器对象
from collections import Iterablefrom collections import Iteratorclass MyList(object): def __init__(self): self.my_list = list() def append_item(self, item): self.my_list.append(item) def __iter__(self): my_iterator = MyIterator(self.my_list) return my_iteratorclass MyIterator(object): def __init__(self, my_list): self.my_list = my_list self.current_index = 0 result = isinstance(self, Iterator) print("MyIterator创立的对象是否是迭代器:", result) def __iter__(self): return self def __next__(self): if self.current_index < len(self.my_list): self.current_index += 1 return self.my_list[self.current_index - 1] else: raise StopIterationmy_list = MyList()my_list.append_item(1)my_list.append_item(2)result = isinstance(my_list, Iterable)print(result)for value in my_list: print(value)
运行后果:
TrueMyIterator创立的对象是否是迭代器: True12
2.2 iter()函数与next()函数
iter函数
: 获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__
办法
next函数
: 获取迭代器中下一个值,会调用迭代器对象身上的__next__
办法
class MyList(object): def __init__(self): self.my_list = list() def append_item(self, item): self.my_list.append(item) def __iter__(self): my_iterator = MyIterator(self.my_list) return my_iteratorclass MyIterator(object): def __init__(self, my_list): self.my_list = my_list self.current_index = 0 def __iter__(self): return self def __next__(self): if self.current_index < len(self.my_list): self.current_index += 1 return self.my_list[self.current_index - 1] else: raise StopIterationmy_list = MyList()my_list.append_item(1)my_list.append_item(2)my_iterator = iter(my_list)print(my_iterator)while True: try: value = next(my_iterator) print(value) except StopIteration as e: break
2.3 for循环的实质
遍历的是可迭代对象
for item in Iterable 循环的实质就是先通过iter()函数获取可迭代对象Iterable的迭代器,而后对获取到的迭代器一直调用next()办法来获取下一个值并将其赋值给item,当遇到StopIteration的异样后循环完结。
遍历的是迭代器
for item in Iterator 循环的迭代器,一直调用next()办法来获取下一个值并将其赋值给item,当遇到StopIteration的异样后循环完结。
2.4 迭代器的利用场景
咱们发现迭代器最外围的性能就是能够通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据汇合中读取的,而是通过程序依照肯定的法则计算生成的,那么也就意味着能够不必再依赖一个已有的数据汇合,也就是说不必再将所有要迭代的数据都一次性缓存下来供后续顺次读取,这样能够节俭大量的存储(内存)空间。
举个例子,比方,数学中有个驰名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加失去:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
当初咱们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。那么这个斐波那契数列咱们就能够用迭代器来实现,每次迭代都通过数学计算来生成下一个数。
class Fibonacci(object): def __init__(self, num): self.num = num self.a = 0 self.b = 1 self.current_index = 0 def __iter__(self): return self def __next__(self): if self.current_index < self.num: result = self.a self.a, self.b = self.b, self.a + self.b self.current_index += 1 return result else: raise StopIterationfib = Fibonacci(5)for value in fib: print(value)
执行后果:
01123
小结
迭代器的作用就是是记录以后数据的地位以便获取下一个地位的值
3、生成器
3.1 生成器的概念
生成器是一类非凡的迭代器,它不须要再像下面的类一样写__iter__()和__next__()
办法了, 应用更加不便,它仍然能够应用next函数和for循环取值
3.2 创立生成器办法1
第一种办法很简略,只有把一个列表生成式的 [ ] 改成 ( )
my_list = [i * 2 for i in range(5)]print(my_list)my_generator = (i * 2 for i in range(5))print(my_generator)for value in my_generator: print(value)
执行后果:
[0, 2, 4, 6, 8]<generator object <genexpr> at 0x101367048>02468
3.3 创立生成器办法2
在def函数外面看到有yield关键字那么就是生成器
def fibonacci(num): a = 0 b = 1 current_index = 0 print("--11---") while current_index < num: result = a a, b = b, a + b current_index += 1 print("--22---") yield result print("--33---")fib = fibonacci(5)value = next(fib)print(value)value = next(fib)print(value)value = next(fib)print(value)
在应用生成器实现的形式中,咱们将本来在迭代器__next__
办法中实现的根本逻辑放到一个函数中来实现,然而将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。
简略来说:只有在def中有yield关键字的 就称为 生成器
3.4 生成器应用return关键字
def fibonacci(num): a = 0 b = 1 current_index = 0 print("--11---") while current_index < num: result = a a, b = b, a + b current_index += 1 print("--22---") yield result print("--33---") return "嘻嘻"fib = fibonacci(5)value = next(fib)print(value)try: value = next(fib) print(value)except StopIteration as e: print(e.value)
提醒:
生成器外面应用return关键字语法上没有问题,然而代码执行到return语句会进行迭代,抛出进行迭代异样。
3.5 yield和return的比照
应用了yield关键字的函数不再是函数,而是生成器。(应用了yield的函数就是生成器)
代码执行到yield会暂停,而后把后果返回进来,下次启动生成器会在暂停的地位持续往下执行
每次启动生成器都会返回一个值,屡次启动能够返回多个值,也就是yield能够返回多个值
return只能返回一次值,代码执行到return语句就进行迭代,抛出进行迭代异样
3.6 应用send办法启动生成器并传参
send办法启动生成器的时候能够传参数
def gen(): i = 0 while i<5: temp = yield i print(temp) i+=1
执行后果:
In [43]: f = gen()In [44]: next(f)Out[44]: 0In [45]: f.send('haha')hahaOut[45]: 1In [46]: next(f)NoneOut[46]: 2In [47]: f.send('haha')hahaOut[47]: 3In [48]:
留神: 如果第一次启动生成器应用send办法,那么参数只能传入None,个别第一次启动生成器应用next函数
小结
- 生成器创立有两种形式,个别都应用yield关键字办法创立生成器
- yield特点是代码执行到yield会暂停,把后果返回进来,再次启动生成器在暂停的地位持续往下执行
4、协程
4.1 协程的概念
协程,又称微线程,纤程,也称为用户级线程,在不开拓线程的根底上实现多任务,也就是在单线程的状况下实现多任务,多个工作依照肯定程序交替执行 艰深了解只有在def外面只看到一个yield关键字示意就是协程
协程是也是实现多任务的一种形式
协程yield的代码实现
简略实现协程
import timedef work1(): while True: print("----work1---") yield time.sleep(0.5)def work2(): while True: print("----work2---") yield time.sleep(0.5)def main(): w1 = work1() w2 = work2() while True: next(w1) next(w2)if __name__ == "__main__": main()
运行后果:
----work1-------work2-------work1-------work2-------work1-------work2-------work1-------work2-------work1-------work2-------work1-------work2---...省略...
小结
协程之间执行工作依照肯定程序交替执行
5、greenlet
5.1 greentlet的介绍
为了更好应用协程来实现多任务,python中的greenlet模块对其封装,从而使得切换工作变的更加简略
应用如下命令装置greenlet模块:
pip3 install greenlet
应用协程实现多任务
import timeimport greenlet# 工作1def work1(): for i in range(5): print("work1...") time.sleep(0.2) # 切换到协程2外面执行对应的工作 g2.switch()# 工作2def work2(): for i in range(5): print("work2...") time.sleep(0.2) # 切换到第一个协程执行对应的工作 g1.switch()if __name__ == '__main__': # 创立协程指定对应的工作 g1 = greenlet.greenlet(work1) g2 = greenlet.greenlet(work2) # 切换到第一个协程执行对应的工作 g1.switch()
运行成果
work1...work2...work1...work2...work1...work2...work1...work2...work1...work2...
6、gevent
6.1 gevent的介绍
greenlet曾经实现了协程,然而这个还要人工切换,这里介绍一个比greenlet更弱小而且可能主动切换工作的第三方库,那就是gevent。
gevent外部封装的greenlet,其原理是当一个greenlet遇到IO(指的是input output 输入输出,比方网络、文件操作等)操作时,比方拜访网络,就主动切换到其余的greenlet,等到IO操作实现,再在适当的时候切换回来继续执行。
因为IO操作十分耗时,常常使程序处于期待状态,有了gevent为咱们主动切换协程,就保障总有greenlet在运行,而不是期待IO
装置
pip3 install gevent
6.2 gevent的应用
import geventdef work(n): for i in range(n): print(gevent.getcurrent(), i)g1 = gevent.spawn(work, 5)g2 = gevent.spawn(work, 5)g3 = gevent.spawn(work, 5)g1.join()g2.join()g3.join()
运行后果
<Greenlet "Greenlet-0" at 0x26d8c970488: work(5)> 0<Greenlet "Greenlet-1" at 0x26d8c970598: work(5)> 0<Greenlet "Greenlet-2" at 0x26d8c9706a8: work(5)> 0<Greenlet "Greenlet-0" at 0x26d8c970488: work(5)> 1<Greenlet "Greenlet-1" at 0x26d8c970598: work(5)> 1<Greenlet "Greenlet-2" at 0x26d8c9706a8: work(5)> 1<Greenlet "Greenlet-0" at 0x26d8c970488: work(5)> 2<Greenlet "Greenlet-1" at 0x26d8c970598: work(5)> 2<Greenlet "Greenlet-2" at 0x26d8c9706a8: work(5)> 2<Greenlet "Greenlet-0" at 0x26d8c970488: work(5)> 3<Greenlet "Greenlet-1" at 0x26d8c970598: work(5)> 3<Greenlet "Greenlet-2" at 0x26d8c9706a8: work(5)> 3<Greenlet "Greenlet-0" at 0x26d8c970488: work(5)> 4<Greenlet "Greenlet-1" at 0x26d8c970598: work(5)> 4<Greenlet "Greenlet-2" at 0x26d8c9706a8: work(5)> 4
能够看到,3个greenlet是顺次运行而不是交替运行
6.3 gevent切换执行
import geventdef work(n): for i in range(n): print(gevent.getcurrent(), i) gevent.sleep(1)g1 = gevent.spawn(work, 5)g2 = gevent.spawn(work, 5)g3 = gevent.spawn(work, 5)g1.join()g2.join()g3.join()
运行后果
<Greenlet at 0x7fa70ffa1c30: f(5)> 0<Greenlet at 0x7fa70ffa1870: f(5)> 0<Greenlet at 0x7fa70ffa1eb0: f(5)> 0<Greenlet at 0x7fa70ffa1c30: f(5)> 1<Greenlet at 0x7fa70ffa1870: f(5)> 1<Greenlet at 0x7fa70ffa1eb0: f(5)> 1<Greenlet at 0x7fa70ffa1c30: f(5)> 2<Greenlet at 0x7fa70ffa1870: f(5)> 2<Greenlet at 0x7fa70ffa1eb0: f(5)> 2<Greenlet at 0x7fa70ffa1c30: f(5)> 3<Greenlet at 0x7fa70ffa1870: f(5)> 3<Greenlet at 0x7fa70ffa1eb0: f(5)> 3<Greenlet at 0x7fa70ffa1c30: f(5)> 4<Greenlet at 0x7fa70ffa1870: f(5)> 4<Greenlet at 0x7fa70ffa1eb0: f(5)> 4
6.4 给程序打补丁
import geventimport timefrom gevent import monkeymonkey.patch_all()def work1(num): for i in range(num): print("work1....") time.sleep(0.2)def work2(num): for i in range(num): print("work2....") time.sleep(0.2) if __name__ == '__main__': g1 = gevent.spawn(work1, 3) g2 = gevent.spawn(work2, 3) g1.join() g2.join()
运行后果
work1....work2....work1....work2....work1....work2....
6.5 留神
以后程序是一个死循环并且还能有耗时操作,就不须要加上join办法了,因为程序须要始终运行不会退出
示例代码
import geventimport timefrom gevent import monkeymonkey.patch_all()def work1(num): for i in range(num): print("work1....") time.sleep(0.2) def work2(num): for i in range(num): print("work2....") time.sleep(0.2) if __name__ == '__main__': g1 = gevent.spawn(work1, 3) g2 = gevent.spawn(work2, 3) while True: print("主线程中执行") time.sleep(0.5)
执行后果:
主线程中执行work1....work2....work1....work2....work1....work2....主线程中执行主线程中执行主线程中执行..省略..
如果应用的协程过多,如果想启动它们就须要一个一个的去应用join()办法去阻塞主线程,这样代码会过于冗余,能够应用gevent.joinall()办法启动须要应用的协程
实例代码
import timeimport geventdef work1(): for i in range(5): print("work1工作了{}".format(i)) gevent.sleep(1)def work2(): for i in range(5): print("work2工作了{}".format(i)) gevent.sleep(1)if __name__ == '__main__': w1 = gevent.spawn(work1) w2 = gevent.spawn(work2) gevent.joinall([w1, w2])
7、过程、线程、协程比照
7.1 过程、线程、协程之间的关系
- 一个过程至多有一个线程,过程外面能够有多个线程
- 一个线程外面能够有多个协程
7.2 过程、线程、线程的比照
- 过程是资源分配的单位
- 线程是操作系统调度的单位
- 过程切换须要的资源最大,效率很低
- 线程切换须要的资源个别,效率个别(当然了在不思考GIL的状况下)
- 协程切换工作资源很小,效率高
- 多过程、多线程依据cpu核数不一样可能是并行的,然而协程是在一个线程中 所以是并发
小结
1. 过程、线程、协程都是能够实现多任务的,能够依据本人理论开发的须要抉择应用
2. 因为线程、协程须要的资源很少,所以应用线程和协程的几率最大
3. 开拓协程须要的资源起码
最近刚好整顿了Python书籍材料电子版等材料,都打包好了,须要的小伙伴可间接返回gzh【Python编程学习圈】支付,关注回复关键词【学习材料】即可收费获取,蕴含有入门到进阶、爬虫、数据分析等方向的材料教程,不要错过啦!