1. 程式人生 > >更深入理解 Python 中的迭代

更深入理解 Python 中的迭代

(點選上方公眾號,可快速關注)

編譯: linux中國 / MjSeven  英文: Trey Hunner

https://linux.cn/article-9681-1.html

深入探討 Python 的 for 迴圈來看看它們在底層如何工作,以及為什麼它們會按照它們的方式工作。

Python 的 for 迴圈不會像其他語言中的 for 迴圈那樣工作。在這篇文章中,我們將深入探討 Python 的 for 迴圈來看看它們在底層如何工作,以及為什麼它們會按照它們的方式工作。

迴圈的問題

我們將通過看一些“陷阱”開始我們的旅程,在我們瞭解迴圈如何在 Python 中工作之後,我們將再次看看這些問題並解釋發生了什麼。

問題 1:迴圈兩次

假設我們有一個數字列表和一個生成器,生成器會返回這些數字的平方:

>>> numbers = [1,2,3,5,7]

>>> squares = (n**2forninnumbers)

我們可以將生成器物件傳遞給 tuple 構造器,從而使其變為一個元組:

>>> tuple(squares)

(1,4,9,25,49)

如果我們使用相同的生成器物件並將其傳給 sum 函式,我們可能會期望得到這些數的和,即 88

>>> sum(squares)

0

但是我們得到了 0

問題 2:包含的檢查

讓我們使用相同的數字列表和相同的生成器物件

>>> numbers = [1,2,3,5,7]

>>> squares = (n**2forninnumbers)

如果我們詢問 9 是否在 squares 生成器中,Python 將會告訴我們 9 在 squares 中。但是如果我們再次詢問相同的問題,Python 會告訴我們 9 不在 squares 中。

>>> 9insquares

True

>>> 9insquares

False

我們詢問相同的問題兩次,Python 給了兩個不同的答案。

問題 3 :拆包

這個字典有兩個鍵值對:

>>> counts = {'apples': 2, 'oranges': 1}

讓我們使用多個變數來對這個字典進行拆包:

>>> x, y = counts

你可能會期望當我們對這個字典進行拆包時,我們會得到鍵值對或者得到一個錯誤。

但是解包字典不會引發錯誤,也不會返回鍵值對。當你解包一個字典時,你會得到鍵:

>>> x

'apples'

回顧:Python 的 for 迴圈

在我們瞭解一些關於這些 Python 片段的邏輯之後,我們將回到這些問題。

Python 沒有傳統的 for 迴圈。為了解釋我的意思,讓我們看一看另一種程式語言的 for 迴圈。

這是一種傳統 C 風格的 for 迴圈,用 JavaScript 編寫:

let numbers = [1,2,3,5,7];

for(leti = 0;i < numbers.length;i += 1){

print(numbers[i])

}

JavaScript、 C、 C++、 Java、 PHP 和一大堆其他程式語言都有這種風格的 for 迴圈,但是 Python 確實沒有

Python 確實沒有 傳統 C 風格的 for 迴圈。在 Python 中確實有一些我們稱之為 for 迴圈的東西,但是它的工作方式類似於 foreach 迴圈。

這是 Python 的 for 迴圈的風格:

numbers = [1,2,3,5,7]

forninnumbers:

print(n)

與傳統 C 風格的 for 迴圈不同,Python 的 for 迴圈沒有索引變數,沒有索引變數初始化,邊界檢查,或者索引遞增。Python 的 for 迴圈完成了對我們的 numbers 列表進行遍歷的所有工作。

因此,當我們在 Python 中確實有 for 迴圈時,我們沒有傳統 C 風格的 for 迴圈。我們稱之為 for 迴圈的東西的工作機制與之相比有很大的不同。

定義:可迭代和序列

既然我們已經解決了 Python 世界中無索引的 for 迴圈,那麼讓我們在此之外來看一些定義。

可迭代是任何你可以用 Python 中的 for 迴圈遍歷的東西。可迭代意味著可以遍歷,任何可以遍歷的東西都是可迭代的。

foritem insome_iterable:

print(item)

序列是一種非常常見的可迭代型別,列表,元組和字串都是序列。

>>> numbers = [1,2,3,5,7]

>>> coordinates = (4,5,7)

>>> words = "hello there"

序列是可迭代的,它有一些特定的特徵集。它們可以從 0 開始索引,以小於序列的長度結束,它們有一個長度並且它們可以被切分。列表,元組,字串和其他所有序列都是這樣工作的。

>>> numbers[0]

1

>>> coordinates[2]

7

>>> words[4]

'o'

Python 中很多東西都是可迭代的,但不是所有可迭代的東西都是序列。集合、字典、檔案和生成器都是可迭代的,但是它們都不是序列。

>>> my_set = {1,2,3}

>>> my_dict = {'k1': 'v1','k2': 'v2'}

>>> my_file = open('some_file.txt')

>>> squares = (n**2forninmy_set)

因此,任何可以用 for 迴圈遍歷的東西都是可迭代的,序列只是一種可迭代的型別,但是 Python 也有許多其他種類的迭代器。

Python 的 for 迴圈不使用索引

你可能認為,Python 的 for 迴圈在底層使用了索引進行迴圈。在這裡我們使用 while 迴圈和索引手動遍歷:

numbers = [1,2,3,5,7]

i = 0

whilei < len(numbers):

print(numbers[i])

i += 1

這適用於列表,但它不會對所有東西都起作用。這種迴圈方式只適用於序列

如果我們嘗試用索引去手動遍歷一個集合,我們會得到一個錯誤:

>>> fruits = {'lemon','apple','orange','watermelon'}

>>> i = 0

>>> whilei < len(fruits):

...print(fruits[i])

...i += 1

...

Traceback(most recent call last):

File"<stdin>",line2,in <module>

TypeError: 'set'objectdoes notsupport indexing

集合不是序列,所以它們不支援索引。

我們不能使用索引手動對 Python 中的每一個迭代物件進行遍歷。對於那些不是序列的迭代器來說,這是行不通的。

迭代器驅動 for 迴圈

因此,我們已經看到,Python 的 for 迴圈在底層不使用索引。相反,Python 的 for 迴圈使用迭代器

迭代器就是可以驅動可迭代物件的東西。你可以從任何可迭代物件中獲得迭代器,你也可以使用迭代器來手動對它的迭代進行遍歷。

讓我們來看看它是如何工作的。

這裡有三個可迭代物件:一個集合,一個元組和一個字串。

>>> numbers = {1,2,3,5,7}

>>> coordinates = (4,5,7)

>>> words = "hello there"

我們可以使用 Python 的內建 iter 函式來訪問這些迭代器,將一個迭代器傳遞給 iter 函式總會給我們返回一個迭代器,無論我們正在使用哪種型別的迭代器。

>>> iter(numbers)

<set_iterator objectat0x7f2b9271c860>

>>> iter(coordinates)

<tuple_iterator objectat0x7f2b9271ce80<