Python_迭代器_生成器_詳解(16)

一、迭代器

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) 直接生成對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章