文章目錄
一、迭代器
1、迭代器引入
遍歷字符串
首先從傳統的遍歷說起:
for i in "hello":
print(i)
可以看到下圖執行結果,將“hello”逐個打印出來:
遍歷數值
for i in 1234 :
print(i)
下圖執行結果,報錯不是一個迭代對象,那麼什麼是迭代對象了?
iterable 這個 單詞就是迭代的意思
2、迭代對象
-
1、可以通過循環遍歷取出元素的對象就是可迭代對象
-
2、正統點說,擁有__iter__方法和__next__方法的對象就是迭代器
-
3、通過 isinstance() 判斷一個對象是否是可迭代對象
-
4、isinstance(obj,Iterable)
我們來分析一組結果,通過上述提到的 isinstance() 方法
from collections import Iterable # 使用的時候導入
print(isinstance([],list)) # True
print(isinstance([],Iterable)) # True
print(isinstance(123,Iterable))# Fasle
不能發現,通過這個測試,我們可以快速知道是否爲可迭代對象,不用再像之前提到的用具體數據去進行測試。
在這裏,我們知道了如何確定迭代對象
3、迭代器
3.1 什麼是迭代器
__ iter __()方法和 __ next __()方法
-
其實我們可以通過 dir(str),還有 dir (int)可以查看詳細的內置方法,這裏就不具體說明了。
-
幫我我們在迭代遍歷時記錄訪問的位置,以便每次迭代都可以返回下一條數據,擁有__iter__方法和__next__方法的對象就是迭代器
描述 | 說明 |
---|---|
iter () | 可以把可迭代對象中的迭代器取出來,內部調用__iter__方法 |
next() | 函數通過迭代器獲取下一位置的值,內部調用__next__方法 |
3.2 迭代器的操作
先看普通的打印
string = "hello"
for i in string:
print(i)
具有特點:自動打印全部,一個一個取出
再看迭代器的操作:
string = "hello"
iter1 = iter(string)
print(iter1) # <str_iterator object at 0x0000023C4D039710> 對象
print(next(iter1)) # h
print(next(iter1)) # e
print(next(iter1)) # l
print(next(iter1)) # l
print(next(iter1)) # o
具有特點:非自動,需要一個一個調用打印
注意:print(iter1.__ next __()) 打印也是可以的 ,看個人喜愛
注意:字符串 “ hello ” 全部打印出,如果我們繼續取下一個呢?添加一句
可以看到報錯了,這就是迭代取值得特點,無法超出字符串長度
3.3 迭代器的應用場景 [ 到底有什麼用?]
可能會想,一個一個取,迭代器好麻煩,也可以將獲取的迭代對象列表化。
可以看到,將對象裝入到列表,如果調用next()方法,會找不到元素,因爲在這裏已經全部取出了
那麼迭代器到底有什麼用????
節約內存,取得時候在生成數據,python2.7 的時候 range()方法就立刻生成了數據,佔用了大 量的內存空間
3.1 數據類型轉換 例如 list和元組之間的轉換底層就是使用迭代器
str = "hello"
lst1 = list(str)
print(lst1)
tup = ("a","b","c")
lst2 = list(tup)
print(lst2)
3.2 斐波拉切 【自定義迭代器】
class Fib:
def __init__(self,n):
self.n = n # 第幾個數
self.a = 1
self.b = 1
self.index = 0 # 記錄位置
def __iter__(self):
return self # 返回自己 調用自己的__next__()方法
def __next__(self):
if self.index < self.n:
ret = self.a
self.a,self.b = self.b,self.a+self.b
self.index += 1
return ret
else:
raise StopIteration
fib = Fib(5)
iter = iter(fib)
print(list(iter))
可能看完這個,你還是有點懵,到底啥用啊-QAQ!!
來看這個
3.3 點人數 【自定義迭代器】
"""
迭代器實現計數器
"""
class OnlineCount:
def __init__(self):
self.index = 0
def __iter__(self):
return self
def __next__(self):
self.index += 1
return self.index
# 獲取可迭代對象
oc = OnlineCount()
# 通過可的迭代對象直接獲取下一個數據
print(next(oc))
print(next(oc))
print(next(oc))
print(next(oc))
print(next(oc))
我們來看結果,發現,我們每執行print(next(oc))
就打印一個數據,並且還是增加的,如果我們繼續,寫寫不就可以一直加了嗎?
所以迭代器
,假設我編寫了一個操作系統,用戶每次點擊一次,就記錄一次,不是實現了半自動化了嘛~~~~~QAQ
關鍵點 |
---|
1、迭代對象 可以通過dir() isinstance() 來確認 |
2、迭代對象被喚醒next(ret) 會一個一個取出值,全部取出,值不可再調用 |
二、生成器
生成器
生成也是迭代器
的一種
1、舉例說明
這裏有個簡單的函數,想必執行func()
就可以執行了吧,但是你發現了沒
有一個 yield 這是一個 生成器的標誌性東西,表示 當生成器被調用的時候
返回一個 1
def func():
print("--func--")
yield 1
func()
這樣直接執行func()
是不會有結果的
需要這樣執行纔會有結果輸出
def func():
print("--func--")
yield 1
ret = func()
print(ret) # <generator object func at 0x000001E870DFD780> 生成器對象
next(ret)
我們來看看這個結果,不是說 生成器 yield 1
被調用就會返回跟着的結果嗎,我們來
瞅瞅,ret
是一個對象,next()
這個方法是,【函數通過迭代器獲取下一位置的值,內部調用__next__方法】那麼 next(ret)
僅會調用出函數內部及yield
前,非生成器標記的執行程序,而我們 yield 1
將值返回了給函數func()
,它又是ret,所以需要打印。
並且註釋掉,next(ret)
,因爲前面提到了,迭代器會把值抽離出來,那麼繼續調用不會有任何值了
可以看到成功的執行所有
2、生成器 yield 多個之間數據分離
def func():
print("--func--")
yield 1
print("func2")
yield 2
g = func()
print(next(g))
# print(next(g))
結果圖 ,發現只打印第一個yield值及前:
改變下函數:
def func():
print("--func--")
yield 1
print("func2")
yield 2
g = func()
print(next(g))
print(next(g))
全部打印出
生成器之間的yield
,有幾個就調用幾次,可以全部打印出。
3、生成器應用場景
3.1 斐波拉切 [ 前面是迭代器實現 自行對比]
# 生成器實現斐波那契數列
def fib(n):
a,b = 1,1
index = 0
print("----1----")
while index<n:
print("----2-----")
yield a
a,b = b,a+b
index += 1
print("----3-----")
g = fib(5)
print(list(iter(g)))
可以看到執行流程,什麼看的懵?QAQ,哪咱們換一種方法
def fib(n):
a,b = 1,1
index = 0
print("----1----")
while index<n:
print("----2-----")
yield a
a,b = b,a+b
index += 1
print("----3-----")
g = fib(5)
# print(list(iter(g)))
print(next(g))
print(next(g))
print(next(g))
我們就看前3個數好了,不然太長了,不好截圖,
3.2 點人數 [ 生成器 ]
def online_counter():
"""無線次數計數器"""
index = 0
while True:
yield index
index += 1
oc = online_counter() # 獲取生成器對象--> 計數功能
print(oc.__next__())
print(next(oc))
print(next(oc))
print(next(oc))
你看,只要繼續print(next(oc))
可以繼續輸出,無限記數
4、send()方法
那麼用個直觀的例子說明問題:
def generator():
print("a")
count = yield 1
print("----->",count)
print("b")
yield 2
g = generator()
print(g.__next__())
print(g.__next__())
我們來看下count = yield 1
,是否以爲count = 1
,實際上yield 1
優先將值返回給了對象函數,所以爲空值
那麼有沒什麼解決辦法呢?
send 獲取下一個值的效果和 next()基本一致,只是在獲取下一個值的時候,給上一 yield 的位 置傳遞一個數據使用 send 的注意事項:
- (1).第一次使用生成器的時候 是用 next 獲取下一個值
- (2).最後一個 yield 不能接受外部的值
def generator():
print("a")
count = yield 1
print("----->",count)
print("b")
yield 2
g = generator()
print(g.__next__())
print(g.send(10)) # 將 數據傳遞給掛起位置,並且喚醒函數
5、yield from
功能 :yield from 循環遍歷容器類型
def func():
yield from "hello"
yield from range(3)
f = func()
for i in f:
print(i,end=" ")
其實就是爲了簡化代碼而生的,
6、生成器表達式
lst = [i for i in range(1,10)]
print(lst)
iter1 = iter(lst)
print(next(iter1)) # 1
g = (i for i in range(1,10))
print(g) # 生成器對象 <generator object <genexpr> at 0x000001FF2E364938>
print(next(g)) # 1
print(next(g)) # 2
print(list(g)) # [3, 4, 5, 6, 7, 8, 9]
相比較前面,省略了 iter1 = iter(lst)
直接生成對象