最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python 迭代器與生成器的具體使用

 更新時間:2026年06月07日 09:15:45   作者:copyer_xyf  
本文主要介紹了Python 迭代器與生成器的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

本文面向已有前端開發(fā)基礎、正在學習 Python 的開發(fā)者。

迭代器和生成器解決的是同一個問題:數(shù)據(jù)不一定要一次性全部準備好,可以在需要的時候一個一個取出來。前端里最接近的經驗是 for...ofSymbol.iterator、生成器函數(shù) function*yield。

這幾個概念可以先合在一起記:

  • 可迭代對象表示“可以被遍歷的數(shù)據(jù)源”
  • 迭代器表示“真正負責一步一步取值的對象”
  • 生成器表示“用 yield 快速創(chuàng)建出來的迭代器”
    后面的 for 循環(huán),本質上就是先從可迭代對象拿到迭代器,再不斷從迭代器里取下一個值。

一、先把概念邊界講清楚

先記住一條主線:

for item in obj
  -> 先調用 iter(obj) 拿到迭代器
  -> 再不斷調用 next(迭代器)
  -> 遇到 StopIteration 后結束

所以這幾個概念可以這樣分:

概念關注點PythonJavaScript
可迭代對象能不能開始遍歷能被 iter() 接受有 Symbol.iterator
迭代器這次遍歷走到哪里了能被 next() 調用有 next()
迭代協(xié)議遍歷接口怎么約定iter() -> next() -> 結束時拋異常Symbol.iterator -> next()
生成器怎么快速創(chuàng)建迭代器函數(shù)體里寫 yieldfunction* + yield

兩門語言只是接口名字和結束方式不同:

Python:
可迭代對象 -- iter() --> 迭代器 -- next() --> value / StopIteration
JS:
可迭代對象 -- Symbol.iterator() --> 迭代器 -- next() --> { value, done }

最關鍵的邊界是:可迭代對象表示“能開始一次遍歷”,迭代器表示“這次遍歷本身”。列表、字符串這類可迭代對象可以反復遍歷,因為每次都能創(chuàng)建新的迭代器;已經創(chuàng)建出來的迭代器通常只能向前走,取過的值不會自動回到起點。

生成器不算新的遍歷體系,它只是更省事的迭代器寫法。手寫迭代器要自己維護位置和結束條件;生成器用 yield 保存暫停點,每次 next() 都從上一次暫停的位置繼續(xù)執(zhí)行。

是不是有點懵??

二、從 for 循環(huán)看迭代過程

JavaScript 里,一個對象只要實現(xiàn)了 Symbol.iterator,就可以被 for...of 消費。

const names = ["張三", "李四", "王五"];

for (const name of names) {
  console.log(name);
}

如果拆開看,for...of 背后大概做了這些事:

const iterator = names[Symbol.iterator]();

console.log(iterator.next()); // { value: '張三', done: false }
console.log(iterator.next()); // { value: '李四', done: false }
console.log(iterator.next()); // { value: '王五', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

所以前端里有兩層概念:

概念判斷方式作用
iterable有 Symbol.iterator可以交給 for...of
iterator有 next()可以一步一步取值

Python 也有這兩層,只是名字和結束方式不同:

JavaScriptPython
obj[Symbol.iterator]()iter(obj)
iterator.next()next(iterator)
返回 { value, done }返回本次值
done: true 表示結束拋出 StopIteration 表示結束

把這個對照關系記住,后面的 Python 語法就會清楚很多。

三、可迭代對象 iterable

可迭代對象就是:能被 for 循環(huán)遍歷的對象。

names = ['張三', '李四', '王五']
cities = ('北京', '上海', '深圳')
msg = 'hello'

for name in names:
    print(name)

這些對象都能被 for 遍歷,所以它們都是可迭代對象。

從協(xié)議角度看,可迭代對象要能被 iter() 接受:

names = ['張三', '李四', '王五']
msg = 'hello'
age = 18

print(iter(names)) # list_iterator
print(iter(msg))   # str_iterator

# print(iter(age)) # TypeError: 'int' object is not iterable

也可以用 hasattr 粗略觀察:

names = ['張三', '李四', '王五']
msg = 'hello'
age = 18

print(hasattr(names, '__iter__')) # True
print(hasattr(msg, '__iter__'))   # True
print(hasattr(age, '__iter__'))   # False

這里的 __iter__ 是 Python 的魔法方法。平時開發(fā)一般不直接寫 names.__iter__(),而是用內置函數(shù) iter(names)。

obj.__iter__()
  -> 底層魔法方法

iter(obj)
  -> 日常使用方式
  -> 內部會調用 obj.__iter__()

四、迭代器 iterator

調用 iter(可迭代對象) 之后,會得到一個迭代器。

names = ['張三', '李四', '王五']

it = iter(names)

print(next(it)) # 張三
print(next(it)) # 李四
print(next(it)) # 王五
print(next(it)) # StopIteration

迭代器的核心能力是:記住當前取到哪里了,每次 next() 返回下一個值。

也就是說,迭代器內部有狀態(tài),類似一個指針:

初始位置
  -> next() 取第 1 個
  -> next() 取第 2 個
  -> next() 取第 3 個
  -> 沒有數(shù)據(jù)了,拋 StopIteration

如果用 while 手動模擬 for,大概是這樣:

names = ['張三', '李四', '王五']

it = iter(names)

while True:
    try:
        item = next(it)
        print(item)
    except StopIteration:
        break

所以 for item in names 并不神秘,它背后就是:

先調用 iter(names) 得到迭代器
再不斷調用 next(迭代器)
遇到 StopIteration 后結束循環(huán)

迭代器自己也是可迭代對象

迭代器一般也有 __iter__ 方法,并且返回自己。

names = ['張三', '李四', '王五']

it = iter(names)

print(iter(it) is it) # True

這樣設計的原因是:for 循環(huán)第一步一定會調用 iter(x)。如果傳進去的已經是迭代器,iter(迭代器) 必須也能正常工作。

迭代器會被消耗

迭代器不是列表,它是一次性向前取值的過程。

names = ['張三', '李四', '王五']

it = iter(names)

print(next(it)) # 張三

for name in it:
    print(name)

# 只會繼續(xù)輸出:
# 李四
# 王五

前面已經被 next(it) 取走的值,不會在后面的 for 里重新出現(xiàn)。

這點很像前端里已經調用過幾次 iterator.next() 后,再繼續(xù) for...of 或繼續(xù) .next(),狀態(tài)會接著往后走,而不是自動重置。

五、自定義可迭代對象

如果希望自己的類能被 for 遍歷,就要實現(xiàn)迭代器協(xié)議。

需求:讓 Person 實例可以被遍歷,依次取出姓名、年齡、性別、地址。

p1 = Person('張三', 18, '男', '北京昌平')

for item in p1:
    print(item)

寫法一:對象和迭代器分開

這種寫法最清晰:Person 負責保存業(yè)務數(shù)據(jù),PersonIterator 負責遍歷過程。

class Person:
    def __init__(self, name, age, gender, address):
        self.name = name
        self.age = age
        self.gender = gender
        self.address = address

    def __iter__(self):
        # 返回一個專門負責遍歷 Person 的迭代器
        return PersonIterator(self)


class PersonIterator:
    def __init__(self, person):
        # 保存外部傳進來的 Person 對象
        self.person = person
        # 記錄當前取到哪個位置
        self.index = 0
        # 配置要遍歷哪些字段
        self.attrs = [
            person.name,
            person.age,
            person.gender,
            person.address,
        ]

    def __iter__(self):
        # 迭代器的 __iter__ 返回自己
        return self

    def __next__(self):
        if self.index >= len(self.attrs):
            raise StopIteration

        value = self.attrs[self.index]
        self.index += 1
        return value

執(zhí)行:

p1 = Person('張三', 18, '男', '北京昌平')

for item in p1:
    print(item)

輸出:

張三
18

北京昌平

這個寫法適合業(yè)務對象比較復雜的場景。業(yè)務對象和遍歷狀態(tài)分開,Person 不需要關心當前遍歷到第幾個字段。

寫法二:對象自己也是迭代器

也可以讓 Person 同時實現(xiàn) __iter____next__

class Person:
    def __init__(self, name, age, gender, address):
        self.name = name
        self.age = age
        self.gender = gender
        self.address = address
        self.attrs = [name, age, gender, address]

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index >= len(self.attrs):
            raise StopIteration

        value = self.attrs[self.index]
        self.index += 1
        return value

這種寫法代碼更少,但要注意:遍歷狀態(tài)放在對象自己身上。多個地方同時遍歷同一個對象時,更容易相互影響。

學習階段可以先寫這種,真實業(yè)務里更推薦“對象和迭代器分開”,職責更清楚。

六、為什么需要迭代器

迭代器最大的價值是惰性計算:不一次性生成所有結果,而是在需要時才計算下一個。

比如生成斐波那契數(shù)列,如果一次性生成 100000 個數(shù)字并放進列表,內存會越來越大。

def fib_list(total):
    result = []
    a = 0
    b = 1

    for _ in range(total):
        result.append(a)
        a, b = b, a + b

    return result

如果改成迭代器,每次只返回當前這個數(shù):

class Fibo:
    def __init__(self, total):
        self.total = total
        self.index = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= self.total:
            raise StopIteration

        value = self.a
        self.a, self.b = self.b, self.a + self.b
        self.index += 1
        return value

使用:

for number in Fibo(10):
    print(number)

迭代器適合這些場景:

  • 數(shù)據(jù)量很大,不想一次性放進內存
  • 不確定用戶最終會消費多少結果
  • 數(shù)據(jù)來自文件、網(wǎng)絡、數(shù)據(jù)庫游標這類流式來源
  • 每個結果只依賴當前狀態(tài)和上一個狀態(tài)

七、生成器 generator

生成器可以理解成:Python 幫你自動實現(xiàn)迭代器協(xié)議的語法糖。

只要一個函數(shù)體里出現(xiàn) yield,這個函數(shù)就不是普通函數(shù),而是生成器函數(shù)。

def demo():
    print('demo 函數(shù)開始執(zhí)行了')
    print(100)
    yield '我是第 1 個 yield 返回的數(shù)據(jù)'

    a = 200
    print(a)
    yield '我是第 2 個 yield 返回的數(shù)據(jù)'

    b = 300
    print(b)
    return '執(zhí)行結束'

調用生成器函數(shù)時,函數(shù)體不會立刻執(zhí)行,而是返回一個生成器對象。

d = demo()

print(hasattr(d, '__iter__')) # True
print(hasattr(d, '__next__')) # True

生成器對象本質上是一種迭代器,所以可以用 next() 取值:

d = demo()

print(next(d))
print(next(d))

try:
    print(next(d))
except StopIteration as e:
    print(e.value) # 執(zhí)行結束

執(zhí)行過程可以這樣理解:

第一次 next()
  -> 函數(shù)從開頭執(zhí)行
  -> 遇到第一個 yield 暫停
  -> yield 后面的值作為本次 next() 的返回值

第二次 next()
  -> 從上次暫停的位置繼續(xù)執(zhí)行
  -> 遇到第二個 yield 再暫停

第三次 next()
  -> 繼續(xù)執(zhí)行
  -> 遇到 return
  -> 拋 StopIteration
  -> return 后面的值會放到異常對象的 value 里

生成器和普通函數(shù)最大的差異是:普通函數(shù)一次調用跑到底,生成器函數(shù)可以在 yield 處暫停,下次再接著跑。

前端里可以對照 function*

function* demo() {
  console.log("demo 開始執(zhí)行");
  yield "第 1 個值";
  yield "第 2 個值";
}

const d = demo();

console.log(d.next());
console.log(d.next());
console.log(d.next());

八、yield 的幾個常見寫法

yield 寫在循環(huán)里

最常見的生成器寫法,是在循環(huán)里不斷 yield。

def fib(total):
    a = 0
    b = 1

    for _ in range(total):
        yield a
        a, b = b, a + b

使用:

for number in fib(10):
    print(number)

這比手寫 class Fibo 簡潔很多,但效果類似:每次需要下一個值時,才繼續(xù)往后計算。

yield from

yield from 可以把另一個可迭代對象里的值依次產出。

def demo():
    nums = [10, 20, 30, 40]
    yield from nums

它大致等價于:

def demo():
    nums = [10, 20, 30, 40]

    for num in nums:
        yield num

所以 yield from 可以記成:

把某個可迭代對象里的數(shù)據(jù),一個一個 yield 出去

send()

生成器除了能往外吐值,也能在繼續(xù)執(zhí)行時接收外部傳進來的值。

def demo():
    print('demo 函數(shù)開始執(zhí)行了')

    a = yield '第 1 個 yield 的返回值'
    print(f'a 接收到:{a}')

    b = yield '第 2 個 yield 的返回值'
    print(f'b 接收到:')

使用:

d = demo()

print(next(d))        # 先啟動生成器,停在第一個 yield
print(d.send('張三')) # 把 '張三' 傳給變量 a,然后繼續(xù)執(zhí)行

try:
    d.send('李四')     # 把 '李四' 傳給變量 b,然后繼續(xù)執(zhí)行到函數(shù)結束
except StopIteration:
    print('生成器執(zhí)行結束')

注意:第一次啟動生成器時不能直接傳普通值,因為代碼還沒有運行到任何一個 yield 位置,沒有地方接收這個值。

d = demo()

# d.send('張三') # TypeError
d.send(None)    # 等價于 next(d)

next() 只能取值;send(value) 既能讓生成器繼續(xù)執(zhí)行,也能把值傳回上一次暫停的 yield 表達式。

九、生成器表達式

生成器表達式是一種快速創(chuàng)建生成器對象的寫法,長得很像列表推導式。

nums = [10, 20, 30, 40]

result1 = [n * 2 for n in nums]
result2 = (n * 2 for n in nums)

print(result1) # [20, 40, 60, 80]
print(result2) # <generator object ...>

區(qū)別在于:

寫法結果是否立刻生成全部結果
[n * 2 for n in nums]列表
(n * 2 for n in nums)生成器對象

生成器表達式適合“每個結果只依賴當前元素”的場景。

nums = [10, 20, 30, 40]

result = (n * 2 for n in nums)

for item in result:
    print(item)

它不會一次性創(chuàng)建 [20, 40, 60, 80],而是每次循環(huán)時才計算當前這個 item。

如果數(shù)據(jù)量很小,并且后面要反復使用結果,列表推導式更直觀。如果數(shù)據(jù)量很大,只需要順序消費一遍,生成器表達式更省內存。

十、最后怎么選

可以按這個順序判斷:

只是遍歷已有 list / tuple / dict / str
  -> 直接 for

想讓自己的類能被 for
  -> 實現(xiàn) __iter__
  -> 如果要自己控制取值過程,再實現(xiàn) __next__

要一個一個惰性產出結果
  -> 優(yōu)先寫生成器函數(shù) yield

只是把一個可迭代對象映射成另一個惰性結果
  -> 用生成器表達式

需要復雜狀態(tài)、多個方法、可維護的對象封裝
  -> 手寫迭代器類

最容易混淆的點:

問題結論
能 for 的一定是迭代器嗎不一定,可能只是可迭代對象
迭代器能 for 嗎能,因為迭代器的 __iter__ 返回自己
iter(obj) 做了什么調用 obj.__iter__(),拿到迭代器
next(it) 做了什么調用 it.__next__(),拿下一個值
取完后怎么結束Python 拋 StopIteration
生成器是什么用 yield 自動創(chuàng)建出來的迭代器
生成器會立刻執(zhí)行函數(shù)體嗎不會,第一次 next() 才開始執(zhí)行
迭代器能重復遍歷嗎通常不能,它會被消耗

到此這篇關于Python 迭代器與生成器的具體使用的文章就介紹到這了,更多相關Python 迭代器與生成器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Python解析不規(guī)則JSON數(shù)據(jù)的實戰(zhàn)技巧

    Python解析不規(guī)則JSON數(shù)據(jù)的實戰(zhàn)技巧

    在真實世界的數(shù)據(jù)處理中,我們很少遇到教科書式的標準?JSON,文將為你提供一套從溫和修復到強力解析的完整工具箱,用?Python?輕松馴服這些野性數(shù)據(jù),有需要的小伙伴可以了解下
    2026-01-01
  • Python從Excel讀取數(shù)據(jù)并使用Matplotlib繪制成二維圖像

    Python從Excel讀取數(shù)據(jù)并使用Matplotlib繪制成二維圖像

    本課程實現(xiàn)使用 Python 從 Excel 讀取數(shù)據(jù),并使用 Matplotlib 繪制成二維圖像。這一過程中,將通過一系列操作來美化圖像,最終得到一個可以出版級別的圖像。本課程對于需要書寫實驗報告,學位論文,發(fā)表文章,做報告的學員具有較大價值
    2023-02-02
  • Python自定義簡單圖軸簡單實例

    Python自定義簡單圖軸簡單實例

    這篇文章主要介紹了Python自定義簡單圖軸簡單實例,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Python使用pandasai實現(xiàn)數(shù)據(jù)分析

    Python使用pandasai實現(xiàn)數(shù)據(jù)分析

    本文主要介紹了Python使用pandasai實現(xiàn)數(shù)據(jù)分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • 使用Python構建一個完整的實時數(shù)據(jù)處理平臺

    使用Python構建一個完整的實時數(shù)據(jù)處理平臺

    在當今數(shù)據(jù)驅動的時代,實時數(shù)據(jù)處理能力已成為企業(yè)核心競爭力之一,本文將介紹如何使用Python技術棧構建一個完整的實時數(shù)據(jù)處理平臺,涵蓋從數(shù)據(jù)采集、處理、存儲到可視化展示的全流程,需要的朋友可以參考下
    2026-02-02
  • python?pandas庫讀取excel/csv中指定行或列數(shù)據(jù)

    python?pandas庫讀取excel/csv中指定行或列數(shù)據(jù)

    通過閱讀表格,可以發(fā)現(xiàn)Pandas中提供了非常豐富的數(shù)據(jù)讀寫方法,下面這篇文章主要給大家介紹了關于python利用pandas庫讀取excel/csv中指定行或列數(shù)據(jù)的相關資料,需要的朋友可以參考下
    2022-02-02
  • 解決TensorFlow程序無限制占用GPU的方法

    解決TensorFlow程序無限制占用GPU的方法

    這篇文章主要介紹了解決TensorFlow程序無限制占用GPU的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • Python進階之協(xié)程詳解

    Python進階之協(xié)程詳解

    這篇文章主要為大家介紹了Python進階之協(xié)程,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Python +Selenium解決圖片驗證碼登錄或注冊問題(推薦)

    Python +Selenium解決圖片驗證碼登錄或注冊問題(推薦)

    這篇文章主要介紹了Python Selenium解決圖片驗證碼登錄或注冊問題,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • Pandas數(shù)據(jù)結構中Series屬性詳解

    Pandas數(shù)據(jù)結構中Series屬性詳解

    本文主要介紹了Pandas數(shù)據(jù)結構中Series屬性詳解,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評論

孙吴县| 勐海县| 丹江口市| 茂名市| 安达市| 皮山县| 江陵县| 长岛县| 榕江县| 鲁甸县| 双桥区| 简阳市| 鄱阳县| 永春县| 崇义县| 亚东县| 凤翔县| 广灵县| 庆城县| 桐柏县| 大安市| 阿瓦提县| 乌鲁木齐市| 江陵县| 两当县| 泰来县| 安平县| 茂名市| 班玛县| 呼和浩特市| 育儿| 莱州市| 高邑县| 凌海市| 句容市| 张家界市| 肇东市| 新源县| 台州市| 四子王旗| 台安县|