網站首頁 編程語言 正文
要想創建一個iterator,必須實現一個有__iter__()和__next__()方法的類,類要能夠跟蹤內部狀態并且在沒有元素返回的時候引發StopIteration異常.
這個過程很繁瑣而且違反直覺.Generator能夠解決這個問題.
python generator是一個簡單的創建iterator的途徑.前面講的那些繁瑣的步驟都可以被generator自動完成.
簡單來說,generator是一個能夠返回迭代器對象的函數.
怎樣創建一個python generator?
就像創建一個函數一樣簡單,只不過不使用return 聲明,而是使用yield聲明.
如果一個函數至少包含一個yield聲明(當然它也可以包含其他yield或return),那么它就是一個generator.?
yield和return都會讓函數返回一些東西,區別在于,return聲明徹底結束一個函數,而yield聲明是暫停函數,保存它的所有狀態,并且后續被調用后會繼續執行.
generator函數和普通函數的區別
- generator函數包含一個以上的yield聲明
- generator函數被調用的時候,會返回一個iterator對象,但是函數并不會立即開始執行
- __iter__()和__next__()方法被自動實現,所以可以使用next()函數對返回的此iterator對象進行迭代
- 一旦一個generator 執行到yield語句,generator函數暫停,程序控制流被轉移到調用方
- 在對generator的連續調用之間,generator的本地變量和狀態會被保存
- 最終,generator函數終止,再調用generator會引發StopIteration異常
下面這個例子說明上述全部要點,我們有一個名為my_gen()的函數,它帶有一些yield聲明.
# A simple generator function
def my_gen():
n = 1
print('This is printed first')
# Generator function contains yield statements
yield n
n += 1
print('This is printed second')
yield n
n += 1
print('This is printed at last')
yield n
有趣的是,在這個例子里變量n在每次調用之間都被記住了。和一般函數不同的是,在函數yield之后本地變量沒有被銷毀,而且,generator對象只能被這樣迭代一次。
要想重復上面的過程,需要類似 a = my_gen() 這樣創建另一個generator對象,并對其使用next方法迭代。
注意
:我們可以對generator對象直接使用for循環。
這是因為一個for循環接收一個iterator對象,且使用next()函數迭代它,當遇到StopIteration異常的時候自動停止。
# A simple generator function
def my_gen():
n = 1
print('This is printed first')
# Generator function contains yield statements
yield n
n += 1
print('This is printed second')
yield n
n += 1
print('This is printed at last')
yield n
# Using for loop
# Output:
# This is printed first
# 1
# This is printed second
# 2
# This is printed at last
# 3
for item in my_gen():
print(item)
有循環的python generator
上面的例子沒有實際的應用意義,我們只是為了探究背后原理。
通常來說,generator都是和循環結合實現的,且這個循環帶有一個終止條件。
我們來看一個reverse一個字符串的例子
def rev_str(my_str):
length = len(my_str)
for i in range(length - 1,-1,-1):
yield my_str[i]
# For loop to reverse the string
# Output:
# o
# l
# l
# e
# h
for char in rev_str("hello"):
print(char)
我們在for循環里面使用range()函數來獲取反向順序的index。
generator除了可以應用于string,還可以應用于其它類型的iterator,例如list,tuple等。
python generator 表達式
使用generator表達式可以很容易地創建簡單的generator。
就像lambda函數可以創建匿名函數一樣,generator函數創建一個匿名generator函數。
generator表達式的語法類似于python的list comprehension,只是方括號被替換為了圓括號而已。
list comprehension和generator表達式的主要區別在于,前者產生全部的list,后者每次僅產生一項。
它們有些懶惰,僅在接到請求的時候才會產生輸出。因此,generator表達式比list comprehension更加節省內存。
# Initialize the list
my_list = [1, 3, 6, 10]
# square each term using list comprehension
# Output: [1, 9, 36, 100]
[x**2 for x in my_list]
# same thing can be done using generator expression
# Output: <generator object <genexpr> at 0x0000000002EBDAF8>
(x**2 for x in my_list)
上面的例子中,generator表達式沒有立即產生需要的結果,而是在需要產生item的時候返回一個generator對象。
# Intialize the list
my_list = [1, 3, 6, 10]
a = (x**2 for x in my_list)
# Output: 1
print(next(a))
# Output: 9
print(next(a))
# Output: 36
print(next(a))
# Output: 100
print(next(a))
# Output: StopIteration
next(a)
generator表達式可以在函數內部使用。當這樣使用的時候,圓括號可以丟棄。
python里為什么要使用generator?
1.容易實現
相對于iterator類來說,generator的實現清晰、簡潔。下面是用iterator實現一個2的指數函數
class PowTwo:
def __init__(self, max = 0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n > self.max:
raise StopIteration
result = 2 ** self.n
self.n += 1
return result
generator這樣實現
def PowTwoGen(max = 0):
n = 0
while n < max:
yield 2 ** n
n += 1
因為generator自動跟蹤實現細節,因此更加清晰、簡潔。
2.節省內存
一個函數返回一個序列(sequence)的時候,會在內存里面把這個序列構建好再返回。如果這個序列包含很多數據的話,就過猶不及了。
而如果序列是以generator方式實現的,就是內存友好的,因為他每次只產生一個item。
3.代表無限的stream
generator是一個很棒的表示無限數據流的工具。無限數據流不能被保存在內存里面,并且因為generator每次產生一個item,它就可以表示無限數據流。
下面的代碼可以產生所有的奇數
def all_even():
n = 0
while True:
yield n
n += 2
4.generator流水線(pipeline)
generator可以對一系列操作執行流水線操作。
假設我們有一個快餐連鎖店的日志。日志的第四列是每小時售出的披薩數量,我們想對近5年的這一數據進行求和。
假設所有數據都是字符,不可用的數據都以"N/A"表示,使用generator可以這樣實現
with open('sells.log') as file:
pizza_col = (line[3] for line in file)
per_hour = (int(x) for x in pizza_col if x != 'N/A')
print("Total pizzas sold = ",sum(per_hour))
這個流水線既高效又易讀,并且看起來很酷!:)
原文鏈接:https://blog.csdn.net/m0_59485658/article/details/129017713
相關推薦
- 2022-03-09 c++中STL庫隊列詳細介紹_C 語言
- 2022-08-10 淺談pandas關于查看庫或依賴庫版本的API原理_python
- 2022-07-07 Python常用內置函數和關鍵字使用詳解_python
- 2022-11-18 教你用正則表達式提取數字和小數點_正則表達式
- 2022-05-01 Android?模擬地圖定位功能的實現_Android
- 2022-04-17 css制作一個簡單的上下跳動 動畫
- 2022-01-29 composer 安裝包提示內存不足的解決辦法
- 2022-06-22 android使用intent傳遞參數實現乘法計算_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支