11 迭代器|生成器|協程

文章目錄

  • 迭代器
    • 可迭代對象
    • 可迭代對象的本質
    • iter()函數與 next()函數
    • 迭代器 Iterator
      • 樣例
    • for...in...循環的本質
    • 使用的場景--斐波那契數列
    • list和tuple也可以接收可迭代對象
  • 生成器
    • 簡介
    • 創建生成器
      • 方法一
      • 方法二
      • 總結
    • 使用 send 喚醒
  • 協程
    • 協程和線程差異
    • 簡單實現協程
    • greenlet
    • gevent
      • 安裝
      • gevent 的使用方法
      • 給程序打補丁
      • gevent的常用方法
    • python最新接口
    • 最后的示例
      • 并行下載

迭代器

迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。

可迭代對象

我們已經知道可以對listtuplestr 等類型的數據使用 for…in…的循環語法從其中依次拿到數據進行使用,我們把這樣的過程稱為遍歷,也叫迭代。

我們把可以通過 for…in…這類語句迭代讀取一條數據供我們使用的對象稱之為可迭代對象(Iterable)。

可以使用 isinstance() 判斷該對象是否是一個可迭代的對象

In [50]: from collections.abc import Iterable
In [51]: isinstance([], Iterable)
Out[51]: True
In [52]: isinstance({}, Iterable)
Out[52]: True
In [53]: isinstance('abc', Iterable)
Out[53]: True
In [54]: isinstance(mylist, Iterable)
Out[54]: False
In [55]: isinstance(100, Iterable)
Out[55]: False

可迭代對象的本質

可迭代對象通過__iter__方法向我們提供一個迭代器,我們在迭代一個可迭代對
象的時候,實際上就是先獲取該對象提供的一個迭代器,然后通過這個迭代器來依次獲取對象中的每一個數據. 那么也就是說,一個具備了__iter__方法的對象,就是一個可迭代對象,所以關鍵就在于我們要如何去重寫這一個__iter__方法。

iter()函數與 next()函數

list、tuple 等都是可迭代對象,我們可以通過 iter()函數獲取這些可迭代對象的迭代器。然后我們可以對獲取到的迭代器不斷使用 next()函數來獲取下一條數據。

iter()函數實際上就是調用了可迭代對象的__iter__方法

注意,當我們已經迭代完最后一個數據之后,再次調用 next()函數會拋出StopIteration 的異常,來告訴我們所有數據都已迭代完成,不用再執行 next()函數了

迭代器 Iterator

通過上面的分析,我們已經知道,迭代器是用來幫助我們記錄每次迭代訪問到的位置,當我們對迭代器使用 next()函數的時候,迭代器會向我們返回它所記錄位置的下一個位置的數據。實際上,在使用 next()函數的時候,調用的就是迭代器對象的__next__方法(Python3 中是對象的__next__方法)。所以,我們要想構造一個迭代器,就要實現它的__next__方法。但這還不夠,python 要求迭代器本身也是可迭代的,所以我們還要為迭代器實現__iter__方法,而__iter__方法要返回一個迭代器,迭代器自身正是一個迭代器,所以迭代器的__iter__方法返回自身即可

一個實現了__iter__方法和__next__方法的對象,就是迭代器。

樣例

from collections.abc import Iterable
# 自定義可迭代對象
class MyList:def __init__(self):self.mylist = []def add(self,num):self.mylist.append(num)# 此處是關鍵,定義了這一個之后,就相當于是讓這個對象是可迭代對象def __iter__(self):return MyIterator(self)# 自定義迭代器
class MyIterator:def __init__(self,mylist):self.mylist:MyList = mylistself.current = 0def __iter__(self):return selfdef __next__(self):if self.current > len(self.mylist.mylist) - 1:# 此處一定要實現拋出StopIteration異常raise StopIterationelse:current = self.currentself.current = self.current + 1return self.mylist.mylist[current]if __name__ == '__main__':mylist = MyList()mylist.add(1)mylist.add(2)mylist.add(3)for i in mylist:print(i)print(isinstance(mylist,Iterable))

for…in…循環的本質

for item in Iterable 循環的本質就是先通過 iter()函數獲取可迭代對象 Iterable 的迭代器,然后對獲取到的迭代器不斷調用 next()方法來獲取下一個值并將其賦值給item,當遇到 StopIteration 的異常后循環

使用的場景–斐波那契數列


class FibIterator(object):"""斐波那契數列迭代器"""def __init__(self, n):""":param n: int, 指明生成數列的前 n 個數"""self.n = n# current 用來保存當前生成到數列中的第幾個數了self.current = 0# num1 用來保存前前一個數,初始值為數列中的第一個數 0self.num1 = 0# num2 用來保存前一個數,初始值為數列中的第二個數 1self.num2 = 1def __next__(self):"""被 next()函數調用來獲取下一個數"""if self.current < self.n:num = self.num1self.num1, self.num2 = self.num2, self.num1+self.num2self.current += 1return numelse:raise StopIterationdef __iter__(self):"""迭代器的__iter__返回自身即可"""return selfif __name__ == '__main__':fib = FibIterator(10)for num in fib:print(num, end=" ")

list和tuple也可以接收可迭代對象

除了 for 循環能接收可迭代對象,list、tuple 等也能接收

li = list(FibIterator(15))
print(li)
tp = tuple(FibIterator(6))
print(tp)

生成器

簡介

利用迭代器,我們可以在每次迭代獲取數據(通過 next()方法)時按照特定的規律進行生成。但是我們在實現一個迭代器時,關于當前迭代到的狀態需要我們自己記錄,進而才能根據當前狀態生成下一個數據。為了達到記錄當前狀態,并配合next()函數進行迭代使用,我們可以采用更簡便的語法,即生成器(generator)。生成器是一類特殊的迭代器。

創建生成器

方法一

實際上就是把一個列表生成式的 [ ] 改成 ( )

my_generator = ( x*2 for x in range(5))
print(my_generator)
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))

對于生成器來說,我們可以按照迭代器的使用方法來使用,即可以通過 next()函數、for 循環、list()等方法進行迭代

方法二

generator 非常強大。如果推算的算法比較復雜,用類似列表生成式的 for 循環無法實現的時候,還可以用函數來實現。

在使用生成器實現的方式中,我們將原本在迭代器__next__方法中實現的基本邏輯放到一個函數中來實現,但是將每次迭代返回數值的 return 換成了 yield,此時新定義的函數便不再是函數,而是一個生成器了。簡單來說:只要在 def 中有yield 關鍵字的 就稱為 生成器此時按照調用函數的方式( 案例中為 F = fib(5) )使用生成器就不再是執行函數體了,而是會返回一個生成器對象( 案例中為 F ),然后就可以按照使用迭代器的方式來使用生成器了。

# 含有yield的函數稱為生成器
def fib(n):current = 0num1, num2 = 0, 1while current < n:num = num1num1, num2 = num2, num1 + num2current += 1# print(num,end=' ')yield numreturn 'done'# F是一個生成器,支持next
F=fib(10)print(F)# for i in F:
#     print(i,end=' ')# 迭代生成
l=[ i for i in F ]
print(l)# 想要拿取return當中的值
try:next(F)
except StopIteration as e:print("\n返回值為:{}".format(e.value))

用 for 循環調用 generator 時,發現拿不到 generator 的 return 語句的返回
值。如果想要拿到返回值,必須捕獲 StopIteration 錯誤,返回值包含在
StopIteration 的 value 中

這里還有一個小細節,讀者可能回想為什么我們在使用已經做好的list時候對于list進行for in處理,可以多次處理多次拿值,但是對于我們上文寫的迭代器為什么不行?原因就在于我們實際上list叫做可迭代對象,它實際上所采用的是我們最上面的那種手法,定義兩個類的手法,每次迭代,都會有一個新的迭代器來運行。

總結

  1. 使用了 yield 關鍵字的函數不再是函數,而是生成器。(使用了 yield 的函數
    就是生成器)
  2. yield 關鍵字有兩點作用:
    – 保存當前運行狀態(斷點),然后暫停執行,即將生成器(函數)掛起
    – 將 yield 關鍵字后面表達式的值作為返回值返回,此時可以理解為起到了 return 的作用
  3. 可以使用 next()函數讓生成器從斷點處繼續執行,即喚醒生成器(函數)
  4. Python3 中的生成器可以使用 return 返回最終運行的返回值

使用 send 喚醒

我們除了可以使用 next()函數來喚醒生成器繼續執行外,還可以使用 send()函數來喚醒執行。使用 send()函數的一個好處是可以在喚醒的同時向斷點處傳入一個附加數據

def gen():i=0while i<5:temp=yield iprint(temp)i+=1g=gen()next(g)
g.send('hello')

協程

協程,又稱微線程,纖程。英文名 Coroutine。

協程是 python 個中另外一種實現多任務的方式,只不過比線程更小占用更小執行單元(理解為需要的資源)。 為啥說它是一個執行單元,因為它自帶 CPU 上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。 只要這個過程中保存或恢復 CPU 上下文那么程序還是可以運行的。

通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變量等信息,然后切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,并且切換的次數以及什么時候再切換到原來的函數都由開發者自己確定。

協程和線程差異

在實現多任務時, 線程切換從系統層面遠不止保存和恢復 CPU 上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存 Cache 等等數據,操作系統還會幫你做這些數據的恢復操作。 所以線程的切換非常耗性能。但是協程的切換只是單純的操作 CPU 的上下文,所以一秒鐘切換個上百萬次系統都抗的住。

簡單實現協程

import timedef work1():while True:print("----work1---")yieldtime.sleep(0.5)def work2():while True:print("----work2---")yieldtime.sleep(0.5)def main():w1 = work1()w2 = work2()while True:next(w1)next(w2)if __name__ == "__main__":main()

greenlet

基本的使用方法:

from greenlet import greenlet
import timedef 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()

gevent

greenlet 已經實現了協程,但是這個還的人工切換,是不是覺得太麻煩了,python 還有一個比 greenlet 更強大的并且能夠自動切換任務的模塊 gevent,其原理是當一個 greenlet 遇到 IO(指的是 input output 輸入輸出,比如網絡、文件操作等)操作時,比如訪問網絡,就自動切換到其他的 greenlet,等到 IO 操作完成,再在適當的時候切換回來繼續執行。

由于 IO 操作非常耗時,經常使程序處于等待狀態,有了 gevent 為我們自動切換協程,就保證總有協程在運行,而不是等待 IO時間

參考鏈接

安裝

pip install gevent -i https://pypi.tuna.tsinghua.edu.cn/simple

gevent 的使用方法

gevent.spawn 接口使用方法gevent.spawn(函數名,傳參)
import geventdef f(n):for i in range(n):print(gevent.getcurrent(), i)#用來模擬一個耗時操作,注意不是 time 模塊中的 sleepgevent.sleep(1)g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 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

給程序打補丁

猴子補丁作用:
monkey patch 指的是在執行時動態替換,通常是在 startup 的時候. 用過 gevent 就會知道,會在最開頭的地方 gevent.monkey.patch_all();把標準庫中的 thread/socket 等給替換掉.這樣我們在后面使用 socket的時候能夠跟尋常一樣使用,無需改動不論什么代碼,可是它變成非堵塞的了

from gevent import monkey
import gevent
import random
import time# 這句話是關鍵
monkey.patch_all()def coroutine_work(coroutine_name):for i in range(10):print(coroutine_name, i)time.sleep(random.random())gevent.joinall([gevent.spawn(coroutine_work, "work1"),gevent.spawn(coroutine_work, "work2")
])

gevent的常用方法

常用方法說明
gevent.spawn()創建一個普通的 Greenlet 對象并切換
gevent.spawn_later(seconds=3)延時創建一個普通的 Greenlet 對象并切換
gevent.spawn_raw()創建的協程對象屬于一個組
gevent.getcurrent()返回當前正在執行的 greenlet
gevent.joinall(jobs)將協程任務添加到事件循環,接收一個任務列表
gevent.wait()可以替代 join 函數等待循環結束,也可以傳入協程對象列表
gevent.kill()殺死一個協程
gevent.killall()殺死一個協程列表里的所有協程
monkey.patch_all()非常重要,會自動將 python 的一些標準模塊替換成 gevent框架

python最新接口

鏈接

官方文檔:
鏈接
鏈接

最后的示例

并行下載

from gevent import monkey
import gevent
import urllib.request# 有耗時操作時需要
monkey.patch_all()def my_downLoad(url):print('GET: %s' % url)resp = urllib.request.urlopen(url)data = resp.read()print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad, 'http://www.baidu.com/'),gevent.spawn(my_downLoad, 'http://www.cskaoyan.com/'),gevent.spawn(my_downLoad, 'http://www.qq.com/'),
])

以及

from gevent import monkey
import gevent
import urllib.request#有 IO 才做時需要這一句
monkey.patch_all()def my_downLoad(file_name, url):print('GET: %s' % url)resp = urllib.request.urlopen(url)data = resp.read()with open(file_name, "wb") as f:f.write(data)print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad,"7a082c0dde36eac2205a088397aaf295.jpg",'http://qzs.qq.com/qzone/v6/v6_config/upload/7a082c0dde36eac2205a088397aaf295.jpg'),gevent.spawn(my_downLoad,"da8e974dc_is.jpg",'https://pic1.zhimg.com/da8e974dc_is.jpg'),])

上面的 url 可以換為自己需要下載視頻、音樂、圖片等url

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36950.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36950.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36950.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微PE工具箱實現U盤重裝Windows系統

教程來源 U盤重裝Windows系統&#xff08;微PE工具箱&#xff09;_嗶哩嗶哩_bilibili 加上自己的一丟丟理解&#xff0c;如果你覺得長視頻看了犯困&#xff0c;不如看看我的理解文章說不定能夠幫助到你 準備工作 到這個網站使用迅雷下載免費無插件的官方鏡像MSDN, 我告訴你…

JVM筆記 —— 出現內存溢出錯誤時時如何排查

一、出現內存溢出的幾種情況 內存溢出錯誤分為StackOverflowError和OutOfMemoryError&#xff0c;前者是棧中出現溢出&#xff0c;后者一般是堆或方法區出現溢出&#xff0c;簡稱OOM 1. 棧溢出 StackOverflowError 棧溢出一般都是因為沒有正確的結束遞歸導致的&#xff0c;無…

Linux中安裝MySQL8版本,安裝MySQL步驟,MySQL8離線安裝

Linux中安裝MySQL8版本的步驟如下&#xff1a; 1.檢查下libaio.so.1的位置 [roottdx ]# whereis libaio.so.1 libaio.so: /usr/lib64/libaio.so.1 如果沒有找到該文件 (1).在線安裝 [roottdx ]# yum install -y libaio (2).離線安裝&#xff1a; 上傳之后執行命令安裝&#…

pymysql 庫 - python 操作 mysql

環境&#xff1a; Win10 x64 Python 3.7 PyMySQL 1.0.2 MySQL 8.0.27 1 安裝 pip install pymysql 2 地址 https://pypi.org/project/pymysql/ 3.1 數據庫版本查詢 (search_version.py) import pymysql# 打開數據庫連接 try:db pymysql.connect(hostlocalhost, userr…

python安裝第三方包時報錯:...\lib\site-packages\pip\_vendor\urllib3\response.py...

安裝redis第三方包&#xff1a; pip install redis報錯現象&#xff1a; 解決方法&#xff1a;使用以下命令可成功安裝 pip install redis -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

關于網絡入侵檢測領域使用Spark/Flink等計算框架做分布式

關于網絡入侵檢測領域使用Spark/Flink等計算框架做分布式 0、引言1 基于LightGBM的網絡入侵檢測研究2 基于互信息法的智能化運維系統入侵檢測Spark實現3 基于Spark的車聯網分布式組合深度學習入侵檢測方法4 基于Flink的分布式在線集成學習框架研究5 基于Flink的分布式并行邏輯回…

mongodb基礎

mongodb語法 參考文檔&#xff1a;https://docs.mongodb.com/manual/reference/ BSON Types BSON Type有2種標識符&#xff0c;整形和字符串 類型數值字符串說明Double1“double”String2“string”Object3“object”Array4“array”Binary data5“binData”Undefined6“un…

8.9黃金最新行情走勢分析及短線交易策略

近期有哪些消息面影響黃金走勢&#xff1f;黃金多空該如何研判&#xff1f; ?黃金消息面解析&#xff1a;周三&#xff08;8月9日&#xff09;現貨黃金維持震蕩&#xff0c;目前交投于1930美元附近&#xff0c;隔日現貨黃金盤中震蕩下行&#xff0c;失守1930關口并在美盤時段…

【Spring】-Spring的IoC和DI

作者&#xff1a;學Java的冬瓜 博客主頁&#xff1a;?冬瓜的主頁&#x1f319; 專欄&#xff1a;【Framework】 主要內容&#xff1a;什么是spring&#xff1f;IoC容器是什么&#xff1f;如何使代碼解耦合&#xff1f;IoC的核心原理&#xff0c;IoC的優點。依賴注入/對象裝配/…

【ARM 嵌入式 編譯系列 10 -- GCC 編譯縮減可執行文件 elf 文件大小】

文章目錄 GCC 如何縮減可執行文件size測試代碼 上篇文章&#xff1a;ARM 嵌入式 編譯系列 9-- GCC 編譯符號表&#xff08;Symbol Table&#xff09;的詳細介紹 下篇文章&#xff1a;ARM 嵌入式 編譯系列 10.1 – GCC 編譯縮減可執行文件 elf 文件大小 GCC 如何縮減可執行文件s…

Linux下在qtcreator中創建qt程序

目錄 1、新建項目 2、單工程項目創建 3、多工程項目創建 4、添加子工程&#xff08;基于多工程目錄結構&#xff09; 5、 .pro文件 1、新建項目 切換到“編輯”界面&#xff0c;點擊菜單欄中的“文件”-“新建文件或項目” 2、單工程項目創建 只有一個工程的項目&#…

Axure RP移動端高保真CRM辦公客戶管理系統原型模板及元件庫

Axure RP移動端高保真CRM辦公客戶管理系統原型模板及元件庫&#xff0c;一套典型的移動端辦公工具型APP Axure RP原型模板&#xff0c;可根據實際的產品需求進行擴展&#xff0c;也可以作為移動端原型設計的參考案例。為提升本作品參考價值&#xff0c;在模板設計過程中盡量追求…

chatGPT應用于房地產行業

作為 2023 年的房地產專業人士&#xff0c;您無疑認識到技術對行業的重大影響。近年來&#xff0c;一項技術進步席卷了世界——人工智能。人工智能徹底改變了房地產業務的各個方面&#xff0c;從簡化管理任務到增強客戶互動。 在本文中&#xff0c;我們將探討幾種巧妙的人工智…

HTML 語言簡介

1.概述 HTML 是網頁使用的語言&#xff0c;定義了網頁的結構和內容。瀏覽器訪問網站&#xff0c;其實就是從服務器下載 HTML 代碼&#xff0c;然后渲染出網頁。 HTML 的全名是“超文本標記語言”&#xff08;HyperText Markup Language&#xff09;&#xff0c;上個世紀90年代…

zabbix自動注冊服務器以及部署代理服務器

文章目錄 Zabbix自動注冊服務器及部署代理服務器一.zabbix自動注冊1.什么是自動注冊2.環境準備3.zabbix客戶端配置4.在 Web 頁面配置自動注冊5.驗證自動注冊 二.部署 zabbix 代理服務器1.分布式監控的作用&#xff1a;2.環境部署3.代理服務器配置4.客戶端配置5.web頁面配置5.1 …

MS9940T 國產 模擬前端AFE芯片 11-15 節鋰電池或磷酸鹽電池管理芯片 替代BQ76940

產品簡述 MS9940T 是模擬前端 (AFE) 芯片&#xff0c;支持11 到 15 組電池串聯。通過 I 2 C &#xff0c;主機控制器 可以使用 MS9940T 來實現電池組管理功能&#xff0c;例如監控&#xff08;電池電壓、電池組電流、電池組 溫度&#xff09;、保護&#xff08;控制…

分類預測 | MATLAB實現GWO-BiLSTM-Attention多輸入分類預測

分類預測 | MATLAB實現GWO-BiLSTM-Attention多輸入分類預測 目錄 分類預測 | MATLAB實現GWO-BiLSTM-Attention多輸入分類預測預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.GWO-BiLSTM-Attention 數據分類預測程序 2.代碼說明&#xff1a;基于灰狼優化算法&#xff08…

vuejs 設計與實現 - 組件的實現原理

1.渲染組件 如果是組件則&#xff1a;vnode .type的值是一個對象。如下&#xff1a; const vnode {type: MyComponent,}為了讓渲染器能處理組件類型的虛擬節點&#xff0c;我們還需要在patch函數中對組件類型的虛擬節點進行處理&#xff0c;如下&#xff1a; function patc…

CentOS7.9 禁用22端口,使用其他端口替代

文章目錄 業務場景操作步驟修改sshd配置文件修改SELinux開放給ssh使用的端口修改防火墻&#xff0c;開放新端口重啟sshd生效 相關知識點介紹sshd服務SELinux服務firewall.service服務 業務場景 我們在某市實施交通信控平臺項目&#xff0c;我們申請了一臺服務器&#xff0c;用…

學習Vue:列表渲染(v-for)

在 Vue.js 中&#xff0c;實現動態列表的顯示是非常常見的需求。為了達到這個目的&#xff0c;Vue 提供了 v-for 指令&#xff0c;它允許您迭代一個數組或對象&#xff0c;將其元素渲染為列表。然而&#xff0c;在使用 v-for 時&#xff0c;key 屬性的設置也非常重要&#xff0…