迭代、迭代器肃拜、生成器痴腌、協(xié)程、yield燃领、greenlet士聪、gevent、進(jìn)程線程協(xié)程對(duì)比猛蔽、gevent多任務(wù)圖片下載
1.迭代
<1>迭代的概念
使用for循環(huán)遍歷取值的過(guò)程叫做迭代剥悟,比如:使用for循環(huán)遍歷列表獲取值的過(guò)程
for value in [2, 3, 4]:
print(value)
<2>可迭代對(duì)象
使用for循環(huán)遍歷取值的對(duì)象叫做可迭代對(duì)象, 比如:列表、元組、字典懦胞、集合、range凉泄、字符串
<3>判斷對(duì)象是否是可迭代對(duì)象
先導(dǎo)入collections包中的Iterable類
isinstance的使用
isinstance(o, t), object type
isinstance(對(duì)象躏尉,類型) # 判斷對(duì)象是否是指定類型,返回一個(gè)布爾類型
from collections import Iterable
# 判斷對(duì)象是否是指定類型
result = isinstance((3, 5), Iterable)
print("元組是否是可迭代對(duì)象:", result)
# 提示: 以后還根據(jù)對(duì)象判斷是否是其它類型,比如以后可以判斷函數(shù)里面的參數(shù)是否是自己想要的類型
result = isinstance(5, int)
print("整數(shù)是否是int類型對(duì)象:", result)
<4>自定義可迭代對(duì)象
自定義可迭代對(duì)象: 在類里面定義__iter__方法創(chuàng)建的對(duì)象就是可迭代對(duì)象
自定義可迭代類型代碼
from collections import Iterable
# 自定義可迭代對(duì)象: 在類里面定義__iter__方法創(chuàng)建的對(duì)象就是可迭代對(duì)象
class MyList(object):
? ? def __init__(self):
? ? ? ? self.my_list = list()
? ? # 添加指定元素
? ? def append_item(self, item):
? ? ? ? self.my_list.append(item)
? ? def __iter__(self):
? ? ? ? # 可迭代對(duì)象的本質(zhì):遍歷可迭代對(duì)象的時(shí)候其實(shí)獲取的是可迭代對(duì)象的迭代器后众, 然后通過(guò)迭代器獲取對(duì)象中的數(shù)據(jù)
# 可迭代對(duì)象的本質(zhì): 是通過(guò)迭代器幫助可迭代對(duì)象依次迭代對(duì)象中的每一個(gè)數(shù)據(jù),真正完成獲取數(shù)據(jù)的操作是通過(guò)迭代器完成的
? ? ? ? pass
my_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)
遍歷可迭代對(duì)象依次獲取數(shù)據(jù)需要迭代器
小結(jié)
在類里面提供一個(gè)__iter__創(chuàng)建的對(duì)象是可迭代對(duì)象胀糜,可迭代對(duì)象是需要迭代器完成數(shù)據(jù)迭代的。
2.迭代器
<1>自定義迭代器對(duì)象
自定義迭代器對(duì)象: 在類里面定義__iter__和__next__方法創(chuàng)建的對(duì)象就是迭代器對(duì)象
from collections import Iterable
from collections import Iterator
# 自定義可迭代對(duì)象: 在類里面定義__iter__方法創(chuàng)建的對(duì)象就是可迭代對(duì)象
class MyList(object):
? ? def __init__(self):
? ? ? ? self.my_list = list()
? ? # 添加指定元素
? ? def append_item(self, item):
? ? ? ? self.my_list.append(item)
? ? def __iter__(self):
? ? ? ? # 可迭代對(duì)象的本質(zhì):遍歷可迭代對(duì)象的時(shí)候其實(shí)獲取的是可迭代對(duì)象的迭代器蒂誉, 然后通過(guò)迭代器獲取對(duì)象中的數(shù)據(jù)
? ? ? ? my_iterator = MyIterator(self.my_list)
? ? ? ? return my_iterator
# 自定義迭代器對(duì)象: 在類里面定義__iter__和__next__方法創(chuàng)建的對(duì)象就是迭代器對(duì)象
class MyIterator(object):
? ? def __init__(self, my_list):
? ? ? ? self.my_list = my_list
? ? ? ? # 記錄當(dāng)前獲取數(shù)據(jù)的下標(biāo)
? ? ? ? self.current_index = 0
? ? ? ? # 判斷當(dāng)前對(duì)象是否是迭代器
? ? ? ? result = isinstance(self, Iterator)
? ? ? ? print("MyIterator創(chuàng)建的對(duì)象是否是迭代器:", result)
? ? def __iter__(self):
? ? ? ? return self
? ? # 獲取迭代器中下一個(gè)值
? ? def __next__(self):
? ? ? ? if self.current_index < len(self.my_list):
? ? ? ? ? ? self.current_index += 1
? ? ? ? ? ? return self.my_list[self.current_index - 1]
? ? ? ? else:
? ? ? ? ? ? # 數(shù)據(jù)取完了教藻,需要拋出一個(gè)停止迭代的異常
? ? ? ? ? ? raise StopIteration
my_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)
<2>iter()函數(shù)與next()函數(shù)
iter函數(shù): 獲取可迭代對(duì)象的迭代器,會(huì)調(diào)用可迭代對(duì)象身上的__iter__方法
next函數(shù): 獲取迭代器中下一個(gè)值右锨,會(huì)調(diào)用迭代器對(duì)象身上的__next__方法
# 自定義可迭代對(duì)象: 在類里面定義__iter__方法創(chuàng)建的對(duì)象就是可迭代對(duì)象
class MyList(object):
? ? def __init__(self):
? ? ? ? self.my_list = list()
? ? # 添加指定元素
? ? def append_item(self, item):
? ? ? ? self.my_list.append(item)
? ? def __iter__(self):
? ? ? ? # 可迭代對(duì)象的本質(zhì):遍歷可迭代對(duì)象的時(shí)候其實(shí)獲取的是可迭代對(duì)象的迭代器括堤, 然后通過(guò)迭代器獲取對(duì)象中的數(shù)據(jù)
? ? ? ? my_iterator = MyIterator(self.my_list)
? ? ? ? return my_iterator
# 自定義迭代器對(duì)象: 在類里面定義__iter__和__next__方法創(chuàng)建的對(duì)象就是迭代器對(duì)象
# 迭代器是記錄當(dāng)前數(shù)據(jù)的位置以便獲取下一個(gè)位置的值
class MyIterator(object):
? ? def __init__(self, my_list):
? ? ? ? self.my_list = my_list
? ? ? ? # 記錄當(dāng)前獲取數(shù)據(jù)的下標(biāo)
? ? ? ? self.current_index = 0
? ? def __iter__(self):
? ? ? ? return self
? ? # 獲取迭代器中下一個(gè)值
? ? def __next__(self):
? ? ? ? if self.current_index < len(self.my_list):
? ? ? ? ? ? self.current_index += 1
? ? ? ? ? ? return self.my_list[self.current_index - 1]
? ? ? ? else:
? ? ? ? ? ? # 數(shù)據(jù)取完了,需要拋出一個(gè)停止迭代的異常
? ? ? ? ? ? raise StopIteration
# 創(chuàng)建了一個(gè)自定義的可迭代對(duì)象
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
# 獲取可迭代對(duì)象的迭代器
my_iterator = iter(my_list)
print(my_iterator)
# 獲取迭代器中下一個(gè)值
# value = next(my_iterator)
# print(value)
# 循環(huán)通過(guò)迭代器獲取數(shù)據(jù)
while True:
? ? try:
? ? ? ? value = next(my_iterator)
? ? ? ? print(value)
? ? except StopIteration as e:
? ? ? ? break
<3>for循環(huán)的本質(zhì)
遍歷的是可迭代對(duì)象
? for item in Iterable 循環(huán)的本質(zhì)就是先通過(guò)iter()函數(shù)獲取可迭代對(duì)象Iterable的迭代器绍移,然后對(duì)獲取到的迭代器不斷調(diào)用next()方法來(lái)獲取下一個(gè)值并將其賦值給item悄窃,當(dāng)遇到StopIteration的異常后循環(huán)結(jié)束。
遍歷的是迭代器
? for item in Iterator 循環(huán)的迭代器蹂窖,不斷調(diào)用next()方法來(lái)獲取下一個(gè)值并將其賦值給item轧抗,當(dāng)遇到StopIteration的異常后循環(huán)結(jié)束。
for循環(huán)內(nèi)部自動(dòng)捕獲停止迭代的異常瞬测,而while循環(huán)內(nèi)部沒有自己捕獲
最終取值操作都是通過(guò)迭代器完成的
<4>迭代器的應(yīng)用場(chǎng)景
我們發(fā)現(xiàn)迭代器最核心的功能就是可以通過(guò)next()函數(shù)的調(diào)用來(lái)返回下一個(gè)數(shù)據(jù)值横媚。如果每次返回的數(shù)據(jù)值不是在一個(gè)已有的數(shù)據(jù)集合中讀取的,而是通過(guò)程序按照一定的規(guī)律計(jì)算生成的月趟,那么也就意味著可以不用再依賴一個(gè)已有的數(shù)據(jù)集合灯蝴,也就是說(shuō)不用再將所有要迭代的數(shù)據(jù)都一次性緩存下來(lái)供后續(xù)依次讀取,這樣可以節(jié)省大量的存儲(chǔ)(內(nèi)存)空間孝宗。
舉個(gè)例子绽乔,比如,數(shù)學(xué)中有個(gè)著名的斐波拉契數(shù)列(Fibonacci)碳褒,數(shù)列中第一個(gè)數(shù)為0折砸,第二個(gè)數(shù)為1,其后的每一個(gè)數(shù)都可由前兩個(gè)數(shù)相加得到:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
現(xiàn)在我們想要通過(guò)for...in...循環(huán)來(lái)遍歷迭代斐波那契數(shù)列中的前n個(gè)數(shù)沙峻。那么這個(gè)斐波那契數(shù)列我們就可以用迭代器來(lái)實(shí)現(xiàn)睦授,每次迭代都通過(guò)數(shù)學(xué)計(jì)算來(lái)生成下一個(gè)數(shù)。
迭代器完成斐波那契的好處:
? 節(jié)省內(nèi)存空間摔寨,因?yàn)槊看胃鶕?jù)算法只生成一個(gè)值
? 生成數(shù)列的個(gè)數(shù)沒有上限控制
class Fibonacci(object):
? ? def __init__(self, num):
? ? ? ? # num:表示生成多少fibonacci數(shù)字
? ? ? ? self.num = num
? ? ? ? # 記錄fibonacci前兩個(gè)值
? ? ? ? self.a = 0
? ? ? ? self.b = 1
? ? ? ? # 記錄當(dāng)前生成數(shù)字的索引
? ? ? ? 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 StopIteration
fib = Fibonacci(5)
# value = next(fib)
# print(value)
for value in fib:
? ? print(value)
小結(jié)
迭代器的作用就是是記錄當(dāng)前數(shù)據(jù)的位置以便獲取下一個(gè)位置的值
3.生成器
<1>生成器的概念
生成器是一類特殊的迭代器,它不需要再像上面的類一樣寫__iter__()和__next__()方法了, 使用更加方便,它依然可以使用next函數(shù)和for循環(huán)取值
<2>創(chuàng)建生成器方法1
? 第一種方法很簡(jiǎn)單去枷,只要把一個(gè)列表推導(dǎo)式的 [ ] 改成 ( )
my_list = [i * 2 for i in range(5)]
print(my_list)
# 創(chuàng)建生成器
my_generator = (i * 2 for i in range(5))
print(my_generator)
# next獲取生成器下一個(gè)值
# value = next(my_generator)
# print(value)
for value in my_generator:
? ? print(value)
<3>創(chuàng)建生成器方法2
在def函數(shù)里面看到有yield關(guān)鍵字那么就是生成器
def fibonacci(num):
? ? a = 0
? ? b = 1
? ? # 記錄生成fibonacci數(shù)字的下標(biāo)
? ? current_index = 0
? ? print("--11---")
? ? while current_index < num:
? ? ? ? result = a
? ? ? ? a, b = b, a + b
? ? ? ? current_index += 1
? ? ? ? print("--22---")
? ? ? ? # 代碼執(zhí)行到y(tǒng)ield會(huì)暫停,然后把結(jié)果返回出去,下次啟動(dòng)生成器會(huì)在暫停的位置繼續(xù)往下執(zhí)行
? ? ? ? yield result
? ? ? ? print("--33---")
fib = fibonacci(5)
value = next(fib)
print(value)
value = next(fib)
print(value)
value = next(fib)
print(value)
# for value in fib:
#? ? print(value)
在使用生成器實(shí)現(xiàn)的方式中删顶,我們將原本在迭代器__next__方法中實(shí)現(xiàn)的基本邏輯放到一個(gè)函數(shù)中來(lái)實(shí)現(xiàn)竖螃,但是將每次迭代返回?cái)?shù)值的return換成了yield,此時(shí)新定義的函數(shù)便不再是函數(shù)逗余,而是一個(gè)生成器了特咆。
簡(jiǎn)單來(lái)說(shuō):只要在def中有yield關(guān)鍵字的 就稱為 生成器
<4>生成器使用return關(guān)鍵字
def fibonacci(num):
? ? a = 0
? ? b = 1
? ? # 記錄生成fibonacci數(shù)字的下標(biāo)
? ? current_index = 0
? ? print("--11---")
? ? while current_index < num:
? ? ? ? result = a
? ? ? ? a, b = b, a + b
? ? ? ? current_index += 1
? ? ? ? print("--22---")
? ? ? ? # 代碼執(zhí)行到y(tǒng)ield會(huì)暫停,然后把結(jié)果返回出去录粱,下次啟動(dòng)生成器會(huì)在暫停的位置繼續(xù)往下執(zhí)行
? ? ? ? yield result
? ? ? ? print("--33---")
? ? ? ? return "嘻嘻"
fib = fibonacci(5)
value = next(fib)
print(value)
# 提示: 生成器里面使用return關(guān)鍵字語(yǔ)法上沒有問(wèn)題腻格,但是代碼執(zhí)行到return語(yǔ)句會(huì)停止迭代,拋出停止迭代異常
# 在python3里面可以使用return關(guān)鍵字啥繁,python2不支持
# return 和 yield的區(qū)別
# yield: 每次啟動(dòng)生成器都會(huì)返回一個(gè)值菜职,多次啟動(dòng)可以返回多個(gè)值,也就是yield可以返回多個(gè)值
# return: 只能返回一次值旗闽,代碼執(zhí)行到return語(yǔ)句就停止迭代
try:
? ? value = next(fib)
? ? print(value)
except StopIteration as e:
? ? # 獲取return的返回值
? ? print(e.value)
提示:
? 生成器里面使用return關(guān)鍵字語(yǔ)法上沒有問(wèn)題酬核,但是代碼執(zhí)行到return語(yǔ)句會(huì)停止迭代,拋出停止迭代異常
? 在python3里面可以使用return關(guān)鍵字适室,python2不支持
<5>yield和return的對(duì)比
? 使用了yield關(guān)鍵字的函數(shù)不再是函數(shù)愁茁,而是生成器。(使用了yield的函數(shù)就是生成器)
? 代碼執(zhí)行到y(tǒng)ield會(huì)暫停亭病,然后把結(jié)果返回出去鹅很,下次啟動(dòng)生成器會(huì)在暫停的位置繼續(xù)往下執(zhí)行
? 每次啟動(dòng)生成器都會(huì)返回一個(gè)值,多次啟動(dòng)可以返回多個(gè)值罪帖,也就是yield可以返回多個(gè)值
? return只能返回一次值促煮,代碼執(zhí)行到return語(yǔ)句就停止迭代,拋出停止迭代異常
<6>使用send方法啟動(dòng)生成器并傳參
send方法啟動(dòng)生成器的時(shí)候可以傳參數(shù)
def gen():
i = 0
while i<5:
temp = yield i
print(temp)
i+=1
next? 和 send的區(qū)別:
? next函數(shù)啟動(dòng)生成器不能傳入?yún)?shù)
? send方法啟動(dòng)生成器可以傳入?yún)?shù),但是第一次只能傳入None
注意:
如果第一次啟動(dòng)生成器使用send方法整袁,那么參數(shù)只能傳入None,一般第一次啟動(dòng)生成器使用next函數(shù)
小結(jié)
? 生成器創(chuàng)建有兩種方式菠齿,一般都使用yield關(guān)鍵字方法創(chuàng)建生成器
? yield特點(diǎn)是代碼執(zhí)行到y(tǒng)ield會(huì)暫停,把結(jié)果返回出去坐昙,再次啟動(dòng)生成器在暫停的位置繼續(xù)往下執(zhí)行
4.協(xié)程
<1>協(xié)程的概念
協(xié)程绳匀,又稱微線程,纖程,也稱為用戶級(jí)線程炸客,在不開辟線程的基礎(chǔ)上完成多任務(wù)疾棵,也就是在單線程的情況下完成多任務(wù),多個(gè)任務(wù)按照一定順序交替執(zhí)行 通俗理解只要在def里面只看到一個(gè)yield關(guān)鍵字表示就是協(xié)程
協(xié)程是也是實(shí)現(xiàn)多任務(wù)的一種方式
協(xié)程yield的代碼實(shí)現(xiàn)
import time
def 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()
小結(jié):
協(xié)程之間執(zhí)行任務(wù)按照一定順序交替執(zhí)行
5.greenlet
<1>greentlet的介紹
為了更好使用協(xié)程來(lái)完成多任務(wù)痹仙,python中的greenlet模塊對(duì)其封裝是尔,從而使得切換任務(wù)變的更加簡(jiǎn)單
使用如下命令安裝greenlet模塊:
pip3 install greenlet
import time
import greenlet
# 任務(wù)1
def work1():
? ? for i in range(5):
? ? ? ? print("work1...")
? ? ? ? time.sleep(0.2)
? ? ? ? # 切換到協(xié)程2里面執(zhí)行對(duì)應(yīng)的任務(wù)
? ? ? ? g2.switch()
# 任務(wù)2
def work2():
? ? for i in range(5):
? ? ? ? print("work2...")
? ? ? ? time.sleep(0.2)
? ? ? ? # 切換到第一個(gè)協(xié)程執(zhí)行對(duì)應(yīng)的任務(wù)
? ? ? ? g1.switch()
if __name__ == '__main__':
? ? # 創(chuàng)建協(xié)程指定對(duì)應(yīng)的任務(wù)
? ? g1 = greenlet.greenlet(work1)
? ? g2 = greenlet.greenlet(work2)
? ? # 切換到第一個(gè)協(xié)程執(zhí)行對(duì)應(yīng)的任務(wù)
? ? g1.switch()
6.gevent
<1>gevent的介紹
greenlet已經(jīng)實(shí)現(xiàn)了協(xié)程,但是這個(gè)還要人工切換开仰,這里介紹一個(gè)比greenlet更強(qiáng)大而且能夠自動(dòng)切換任務(wù)的第三方庫(kù)拟枚,那就是gevent薪铜。
gevent內(nèi)部封裝的greenlet,其原理是當(dāng)一個(gè)greenlet遇到IO(指的是input output 輸入輸出恩溅,比如網(wǎng)絡(luò)隔箍、文件操作等)操作時(shí),比如訪問(wèn)網(wǎng)絡(luò)脚乡,就自動(dòng)切換到其他的greenlet蜒滩,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來(lái)繼續(xù)執(zhí)行每窖。
由于IO操作非常耗時(shí)帮掉,經(jīng)常使程序處于等待狀態(tài)弦悉,有了gevent為我們自動(dòng)切換協(xié)程窒典,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO
安裝
pip3 install gevent
<2>gevent的使用
import gevent
def work(n):
? ? for i in range(n):
? ? ? ? # 獲取當(dāng)前協(xié)程gevent.getcurrent()
? ? ? ? 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()
3個(gè)greenlet是依次運(yùn)行而不是交替運(yùn)行
<3>gevent切換執(zhí)行
import gevent
def work(n):
? ? for i in range(n):
? ? ? ? # 獲取當(dāng)前協(xié)程
? ? ? ? print(gevent.getcurrent(), i)
? ? ? ? #用來(lái)模擬一個(gè)耗時(shí)操作稽莉,注意不是time模塊中的sleep
? ? ? ? gevent.sleep(1)
g1 = gevent.spawn(work, 5)
g2 = gevent.spawn(work, 5)
g3 = gevent.spawn(work, 5)
g1.join()
g2.join()
g3.join()
<4>給程序打補(bǔ)丁
gevent默認(rèn)不會(huì)認(rèn)為系統(tǒng)的耗時(shí)操作是耗時(shí)的瀑志,不識(shí)別time.sleep,accept,recv等,需要打補(bǔ)丁
import gevent
import time
from gevent import monkey
# 打補(bǔ)丁污秆,讓gevent框架識(shí)別耗時(shí)操作劈猪,比如:time.sleep,accept,recv,網(wǎng)絡(luò)請(qǐng)求延時(shí)等
monkey.patch_all()
# 任務(wù)1
def work1(num):
? ? for i in range(num):
? ? ? ? print("work1....")
? ? ? ? time.sleep(0.2)
? ? ? ? # gevent.sleep(0.2)
# 任務(wù)1
def work2(num):
? ? for i in range(num):
? ? ? ? print("work2....")
? ? ? ? time.sleep(0.2)
? ? ? ? # gevent.sleep(0.2)
if __name__ == '__main__':
? ? # 創(chuàng)建協(xié)程指定對(duì)應(yīng)的任務(wù)
? ? g1 = gevent.spawn(work1, 3)
? ? g2 = gevent.spawn(work2, 3)
? ? # 主線程等待協(xié)程執(zhí)行完成以后程序再退出
? ? g1.join()
? ? g2.join()
<5>注意
? 當(dāng)前程序是一個(gè)死循環(huán)并且還能有耗時(shí)操作,就不需要加上join方法了,因?yàn)槌绦蛐枰恢边\(yùn)行不會(huì)退出
示例代碼
import gevent
import time
from gevent import monkey
# 打補(bǔ)丁良拼,讓gevent框架識(shí)別耗時(shí)操作战得,比如:time.sleep,網(wǎng)絡(luò)請(qǐng)求延時(shí)
monkey.patch_all()
# 任務(wù)1
def work1(num):
? ? for i in range(num):
? ? ? ? print("work1....")
? ? ? ? time.sleep(0.2)
? ? ? ? # gevent.sleep(0.2)
# 任務(wù)1
def work2(num):
? ? for i in range(num):
? ? ? ? print("work2....")
? ? ? ? time.sleep(0.2)
? ? ? ? # gevent.sleep(0.2)
if __name__ == '__main__':
? ? # 創(chuàng)建協(xié)程指定對(duì)應(yīng)的任務(wù)
? ? g1 = gevent.spawn(work1, 3)
? ? g2 = gevent.spawn(work2, 3)
? ? while True:
? ? ? ? print("主線程中執(zhí)行")
? ? ? ? time.sleep(0.5)
7.進(jìn)程庸推、線程常侦、協(xié)程對(duì)比
<1>進(jìn)程、線程贬媒、協(xié)程之間的關(guān)系
? 一個(gè)進(jìn)程至少有一個(gè)線程聋亡,進(jìn)程里面可以有多個(gè)線程
? 一個(gè)線程里面可以有多個(gè)協(xié)程
<2>進(jìn)程、線程际乘、線程的對(duì)比
? 進(jìn)程是系統(tǒng)資源分配的基本單位
? 線程是操作系統(tǒng)調(diào)度的基本單位
? 進(jìn)程切換需要的資源最大坡倔,效率很低
? 線程切換需要的資源一般,效率一般(當(dāng)然了在不考慮GIL的情況下)
? 協(xié)程切換任務(wù)資源很小脖含,效率高
? 多進(jìn)程罪塔、多線程根據(jù)cpu核數(shù)不一樣可能是并行的,但是協(xié)程是在一個(gè)線程中 所以是并發(fā)
小結(jié)
? 進(jìn)程养葵、線程垢袱、協(xié)程都是可以完成多任務(wù)的,可以根據(jù)自己實(shí)際開發(fā)的需要選擇使用
? 由于線程港柜、協(xié)程需要的資源很少请契,所以使用線程和協(xié)程的幾率最大
? 開辟協(xié)程需要的資源最少
? 協(xié)程和線程的區(qū)別是:協(xié)程避免了無(wú)意義的調(diào)度咳榜,由此可以提高性能,但也因此爽锥,程序員必須自己承擔(dān)調(diào)度的責(zé)任涌韩,同時(shí),協(xié)程也失去了標(biāo)準(zhǔn)線程使用多CPU的能力臣樱。
? 進(jìn)程擁有自己獨(dú)立的堆和棧,既不共享堆腮考,亦不共享?xiàng)9秃粒M(jìn)程由操作系統(tǒng)調(diào)度。
? 線程擁有自己獨(dú)立的棧和共享的堆踩蔚,共享堆棚放,不共享?xiàng)#€程亦由操作系統(tǒng)調(diào)度(標(biāo)準(zhǔn)線程是的)
8.gevent多任務(wù)圖片下載
<1>多任務(wù)圖片下載的示例代碼
import gevent
import urllib.request # 網(wǎng)絡(luò)請(qǐng)求模塊
from gevent import monkey
# 打補(bǔ)断诿觥: 讓gevent使用網(wǎng)絡(luò)請(qǐng)求的耗時(shí)操作飘蚯,讓協(xié)程自動(dòng)切換執(zhí)行對(duì)應(yīng)的下載任務(wù)
monkey.patch_all()
# 根據(jù)圖片地址下載對(duì)應(yīng)的圖片
def download_img(img_url, img_name):
? ? try:
? ? ? ? print(img_url)
? ? ? ? # 根據(jù)圖片地址打開網(wǎng)絡(luò)資源數(shù)據(jù)
? ? ? ? response = urllib.request.urlopen(img_url)
? ? ? ? # 創(chuàng)建文件把數(shù)據(jù)寫入到指定文件里面
? ? ? ? with open(img_name, "wb") as img_file:
? ? ? ? ? ? while True:
? ? ? ? ? ? ? ? # 讀取網(wǎng)絡(luò)圖片數(shù)據(jù)
? ? ? ? ? ? ? ? img_data = response.read(1024)
? ? ? ? ? ? ? ? if img_data:
? ? ? ? ? ? ? ? ? ? # 把數(shù)據(jù)寫入到指定文件里面
? ? ? ? ? ? ? ? ? ? img_file.write(img_data)
? ? ? ? ? ? ? ? else:
? ? ? ? ? ? ? ? ? ? break
? ? except Exception as e:
? ? ? ? print("圖片下載異常:", e)
? ? else:
? ? ? ? print("圖片下載成功: %s" % img_name)
if __name__ == '__main__':
? ? # 準(zhǔn)備圖片地址
? ? img_url1 = "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=551346117,2593226454&fm=27&gp=0.jpg"
? ? img_url2 = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=829730016,3409799239&fm=27&gp=0.jpg"
? ? img_url3 = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1815077192,817368579&fm=27&gp=0.jpg"
? ? # 創(chuàng)建協(xié)程指派對(duì)應(yīng)的任務(wù)
? ? g1 = gevent.spawn(download_img, img_url1, "1.jpg")
? ? g2 = gevent.spawn(download_img, img_url2, "2.jpg")
? ? g3 = gevent.spawn(download_img, img_url3, "3.jpg")
? ? # 主線程等待所有的協(xié)程執(zhí)行完成以后程序再退出
? ? gevent.joinall([g1, g2, g3])
依次根據(jù)圖片地址去下載,但是收到數(shù)據(jù)的先后順序不一定與發(fā)送順序相同福也,這也就體現(xiàn)出了異步局骤,即不確定什么時(shí)候會(huì)收到數(shù)據(jù),順序不一定