日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

今天為大家解讀Python中的for循環(huán),深入探討它們在底層是如何開展工作的,以及他們?yōu)槭裁磿凑兆约旱姆绞焦ぷ鳎麄兣c別的語言中的for循環(huán)有什么不同等問題。

 

循環(huán)的問題

我們將通過看一些“陷阱”開始我們的旅程,在我們了解循環(huán)如何在 Python 中工作之后,我們將再次看看這些問題并解釋發(fā)生了什么。

問題 1:循環(huán)兩次

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

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

我們可以將生成器對象傳遞給 tuple 構(gòu)造器,從而使其變?yōu)橐粋€(gè)元組:

>>> tuple(squares)
(1, 4, 9, 25, 49)

如果我們使用相同的生成器對象并將其傳給 sum 函數(shù),我們可能會期望得到這些數(shù)的和,即 88。

>>> sum(squares)
0

但是我們得到了 0。

問題 2:包含的檢查

讓我們使用相同的數(shù)字列表和相同的生成器對象

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

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

>>> 9 in squares
True
>>> 9 in squares
False

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

問題 3 :拆包

這個(gè)字典有兩個(gè)鍵值對:

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

讓我們使用多個(gè)變量來對這個(gè)字典進(jìn)行拆包:

>>> x, y = counts

你可能會期望當(dāng)我們對這個(gè)字典進(jìn)行拆包時(shí),我們會得到鍵值對或者得到一個(gè)錯(cuò)誤。

但是解包字典不會引發(fā)錯(cuò)誤,也不會返回鍵值對。當(dāng)你解包一個(gè)字典時(shí),你會得到鍵:

>>> x
'apples'

回顧:Python 的 for 循環(huán)

在我們了解一些關(guān)于這些 Python 片段的邏輯之后,我們將回到這些問題。

Python 沒有傳統(tǒng)的 for 循環(huán)。為了解釋我的意思,讓我們看一看另一種編程語言的 for 循環(huán)。

這是一種傳統(tǒng) C 風(fēng)格的 for 循環(huán),用 JAVAScript 編寫:

let numbers = [1, 2, 3, 5, 7];
for (let i = 0; i < numbers.length; i += 1) {
print(numbers[i])
}

JavaScript、 C、 C++、 Java、 php 和一大堆其他編程語言都有這種風(fēng)格的 for 循環(huán),但是 Python 確實(shí)沒有。

Python 確實(shí)沒有 傳統(tǒng) C 風(fēng)格的 for 循環(huán)。在 Python 中確實(shí)有一些我們稱之為 for 循環(huán)的東西,但是它的工作方式類似于 foreach 循環(huán)。

這是 Python 的 for 循環(huán)的風(fēng)格:

numbers = [1, 2, 3, 5, 7]
for n in numbers:
print(n)

與傳統(tǒng) C 風(fēng)格的 for 循環(huán)不同,Python 的 for 循環(huán)沒有索引變量,沒有索引變量初始化,邊界檢查,或者索引遞增。Python 的 for 循環(huán)完成了對我們的 numbers 列表進(jìn)行遍歷的所有工作。

因此,當(dāng)我們在 Python 中確實(shí)有 for 循環(huán)時(shí),我們沒有傳統(tǒng) C 風(fēng)格的 for 循環(huán)。我們稱之為 for 循環(huán)的東西的工作機(jī)制與之相比有很大的不同。

定義:可迭代和序列

既然我們已經(jīng)解決了 Python 世界中無索引的 for 循環(huán),那么讓我們在此之外來看一些定義。

可迭代是任何你可以用 Python 中的 for 循環(huán)遍歷的東西。可迭代意味著可以遍歷,任何可以遍歷的東西都是可迭代的。

for item in some_iterable:
print(item)

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

>>> numbers = [1, 2, 3, 5, 7]
>>> coordinates = (4, 5, 7)
>>> words = "hello there"

序列是可迭代的,它有一些特定的特征集。它們可以從 0 開始索引,以小于序列的長度結(jié)束,它們有一個(gè)長度并且它們可以被切分。列表,元組,字符串和其他所有序列都是這樣工作的。

>>> 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**2 for n in my_set)

因此,任何可以用 for 循環(huán)遍歷的東西都是可迭代的,序列只是一種可迭代的類型,但是 Python 也有許多其他種類的迭代器。

Python 的 for 循環(huán)不使用索引

你可能認(rèn)為,Python 的 for 循環(huán)在底層使用了索引進(jìn)行循環(huán)。在這里我們使用 while 循環(huán)和索引手動遍歷:

numbers = [1, 2, 3, 5, 7]
i = 0
while i < len(numbers):
print(numbers[i])
i += 1

這適用于列表,但它不會對所有東西都起作用。這種循環(huán)方式只適用于序列。

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

>>> fruits = {'lemon', 'apple', 'orange', 'watermelon'}
>>> i = 0
>>> while i < len(fruits):
... print(fruits[i])
... i += 1
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: 'set' object does not support indexing

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

我們不能使用索引手動對 Python 中的每一個(gè)迭代對象進(jìn)行遍歷。對于那些不是序列的迭代器來說,這是行不通的。

迭代器驅(qū)動 for 循環(huán)

因此,我們已經(jīng)看到,Python 的 for 循環(huán)在底層不使用索引。相反,Python 的 for 循環(huán)使用迭代器。

迭代器就是可以驅(qū)動可迭代對象的東西。你可以從任何可迭代對象中獲得迭代器,你也可以使用迭代器來手動對它的迭代進(jìn)行遍歷。

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

這里有三個(gè)可迭代對象:一個(gè)集合,一個(gè)元組和一個(gè)字符串。

>>> numbers = {1, 2, 3, 5, 7}
>>> coordinates = (4, 5, 7)
>>> words = "hello there"

我們可以使用 Python 的內(nèi)置 iter 函數(shù)來訪問這些迭代器,將一個(gè)迭代器傳遞給 iter 函數(shù)總會給我們返回一個(gè)迭代器,無論我們正在使用哪種類型的迭代器。

>>> iter(numbers)
<set_iterator object at 0x7f2b9271c860>
>>> iter(coordinates)
<tuple_iterator object at 0x7f2b9271ce80>
>>> iter(words)
<str_iterator object at 0x7f2b9271c860>

一旦我們有了迭代器,我們可以做的事情就是通過將它傳遞給內(nèi)置的 next 函數(shù)來獲取它的下一項(xiàng)。

>>> numbers = [1, 2, 3]
>>> my_iterator = iter(numbers)
>>> next(my_iterator)
1
>>> next(my_iterator)
2

迭代器是有狀態(tài)的,這意味著一旦你從它們中消耗了一項(xiàng),它就消失了。

如果你從迭代器中請求 next 項(xiàng),但是其中沒有更多的項(xiàng)了,你將得到一個(gè) StopIteration異常:

>>> next(my_iterator)
3
>>> next(my_iterator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

所以你可以從每個(gè)迭代中獲得一個(gè)迭代器,迭代器唯一能做的事情就是用 next 函數(shù)請求它們的下一項(xiàng)。如果你將它們傳遞給 ext,但它們沒有下一項(xiàng)了,那么就會引發(fā) StopIteration 異常。

你可以將迭代器想象成 Pez 分配器(LCTT 譯注:Pez 是一個(gè)結(jié)合玩具的獨(dú)特復(fù)合式糖果),不能重新分配。你可以把 Pez 拿出去,但是一旦 Pez 被移走,它就不能被放回去,一旦分配器空了,它就沒用了。

沒有 for 的循環(huán)

既然我們已經(jīng)了解了迭代器和 iter 以及 next 函數(shù),我們將嘗試在不使用 for 循環(huán)的情況下手動遍歷迭代器。

我們將通過嘗試將這個(gè) for 循環(huán)變?yōu)?while 循環(huán):

def funky_for_loop(iterable, action_to_do):
for item in iterable:
action_to_do(item)

為了做到這點(diǎn),我們需要:

從給定的可迭代對象中獲得迭代器

反復(fù)從迭代器中獲得下一項(xiàng)

如果我們成功獲得下一項(xiàng),就執(zhí)行 for 循環(huán)的主體

如果我們在獲得下一項(xiàng)時(shí)得到了一個(gè) StopIteration 異常,那么就停止循環(huán)

def funky_for_loop(iterable, action_to_do):
iterator = iter(iterable)
done_looping = False
while not done_looping:
try:
item = next(iterator)
except StopIteration:
done_looping = True
else:
action_to_do(item)

我們只是通過使用 while 循環(huán)和迭代器重新定義了 for 循環(huán)。

上面的代碼基本上定義了 Python 在底層循環(huán)的工作方式。如果你理解內(nèi)置的 iter 和 next函數(shù)的遍歷循環(huán)的工作方式,那么你就會理解 Python 的 for 循環(huán)是如何工作的。

事實(shí)上,你不僅僅會理解 for 循環(huán)在 Python 中是如何工作的,所有形式的遍歷一個(gè)可迭代對象都是這樣工作的。

迭代器協(xié)議iterator protocol 是一種很好表示 “在 Python 中遍歷迭代器是如何工作的”的方式。它本質(zhì)上是對 iter 和 ext 函數(shù)在 Python 中是如何工作的定義。Python 中所有形式的迭代都是由迭代器協(xié)議驅(qū)動的。

迭代器協(xié)議被 for 循環(huán)使用(正如我們已經(jīng)看到的那樣):

for n in numbers:
print(n)

多重賦值也使用迭代器協(xié)議:

x, y, z = coordinates

星型表達(dá)式也是用迭代器協(xié)議:

a, b, *rest = numbers
print(*numbers)

許多內(nèi)置函數(shù)依賴于迭代器協(xié)議:

unique_numbers = set(numbers)

在 Python 中任何與迭代器一起工作的東西都可能以某種方式使用迭代器協(xié)議。每當(dāng)你在 Python 中遍歷一個(gè)可迭代對象時(shí),你將依賴于迭代器協(xié)議。

生成器是迭代器

所以你可能會想:迭代器看起來很酷,但它們看起來像一個(gè)實(shí)現(xiàn)細(xì)節(jié),我們作為 Python 的使用者,可能不需要關(guān)心它們。

我有消息告訴你:在 Python 中直接使用迭代器是很常見的。

這里的 squares 對象是一個(gè)生成器:

>>> numbers = [1, 2, 3]
>>> squares = (n**2 for n in numbers)

生成器是迭代器,這意味著你可以在生成器上調(diào)用 next 來獲得它的下一項(xiàng):

>>> next(squares)
1
>>> next(squares)
4

但是如果你以前用過生成器,你可能也知道可以循環(huán)遍歷生成器:

>>> squares = (n**2 for n in numbers)
>>> for n in squares:
... print(n)
...
1
4
9

如果你可以在 Python 中循環(huán)遍歷某些東西,那么它就是可迭代的。

所以生成器是迭代器,但是生成器也是可迭代的,這又是怎么回事呢?

生成器是可迭代的

我再說一遍:Python 中的每一個(gè)迭代器都是可迭代的,意味著你可以循環(huán)遍歷迭代器。

因?yàn)榈饕彩强傻?,所以你可以使用?nèi)置 next 函數(shù)從可迭代對象中獲得迭代器:

>>> numbers = [1, 2, 3]
>>> iterator1 = iter(numbers)
>>> iterator2 = iter(iterator1)

請記住,當(dāng)我們在可迭代對象上調(diào)用 iter 時(shí),它會給我們返回一個(gè)迭代器。

當(dāng)我們在迭代器上調(diào)用 iter 時(shí),它會給我們返回它自己:

>>> iterator1 is iterator2
True

迭代器是可迭代的,所有的迭代器都是它們自己的迭代器。

def is_iterator(iterable):
return iter(iterable) is iterable

迷惑了嗎?

讓我們回顧一些這些措辭。

一個(gè)可迭代對象是你可以迭代的東西。

一個(gè)迭代對象器是一種實(shí)際上遍歷可迭代對象的代理。

此外,在 Python 中迭代器也是可迭代的,它們充當(dāng)它們自己的迭代器。

所以迭代器是可迭代的,但是它們沒有一些可迭代對象擁有的各種特性。

迭代器沒有長度,它們不能被索引:

>>> numbers = [1, 2, 3, 5, 7]
>>> iterator = iter(numbers)
>>> len(iterator)
TypeError: object of type 'list_iterator' has no len()
>>> iterator[0]
TypeError: 'list_iterator' object is not subscriptable

從我們作為 Python 程序員的角度來看,你可以使用迭代器來做的唯一有用的事情是將其傳遞給內(nèi)置的 next 函數(shù),或者對其進(jìn)行循環(huán)遍歷:

>>> next(iterator)
1
>>> list(iterator)
[2, 3, 5, 7]

如果我們第二次循環(huán)遍歷迭代器,我們將一無所獲:

>>> list(iterator)
[]

你可以把迭代器看作是惰性迭代器,它們是一次性使用,這意味著它們只能循環(huán)遍歷一次。

正如你在下面的真值表中所看到的,可迭代對象并不總是迭代器,但是迭代器總是可迭代的:

 

python中的for循環(huán)在底層是如何開展工作的?

 

 

全部的迭代器協(xié)議

讓我們從 Python 的角度來定義迭代器是如何工作的。

可迭代對象可以被傳遞給 iter 函數(shù),以便為它們獲得迭代器。

迭代器:

可以傳遞給 next 函數(shù),它將給出下一項(xiàng),如果沒有下一項(xiàng),那么它將會引發(fā) StopIteration 異常。

可以傳遞給 iter 函數(shù),它會返回一個(gè)自身的迭代器。

這些語句反過來也是正確的:

任何可以在不引發(fā) TypeError 異常的情況下傳遞給 iter 的東西都是可迭代的

任何可以在不引發(fā) TypeError 異常的情況下傳遞給 next 的東西都是一個(gè)迭代器

當(dāng)傳遞給 iter 時(shí),任何返回自身的東西都是一個(gè)迭代器

這就是 Python 中的迭代器協(xié)議。

迭代器的惰性

迭代器允許我們一起工作,創(chuàng)建惰性可迭代對象,即在我們要求它們提供下一項(xiàng)之前,它們不做任何事情。因?yàn)榭梢詣?chuàng)建惰性迭代器,所以我們可以創(chuàng)建無限長的迭代器。我們可以創(chuàng)建對系統(tǒng)資源比較保守的迭代器,可以節(jié)省我們的內(nèi)存,節(jié)省 CPU 時(shí)間。

迭代器無處不在

你已經(jīng)在 Python 中看到過許多迭代器,我也提到過生成器是迭代器。Python 的許多內(nèi)置類型也是迭代器。例如,Python 的 enumerate 和 reversed 對象就是迭代器。

>>> letters = ['a', 'b', 'c']
>>> e = enumerate(letters)
>>> e
<enumerate object at 0x7f112b0e6510>
>>> next(e)
(0, 'a')

在 Python 3 中,zip, map 和 filter 也是迭代器。

>>> numbers = [1, 2, 3, 5, 7]
>>> letters = ['a', 'b', 'c']
>>> z = zip(numbers, letters)
>>> z
<zip object at 0x7f112cc6ce48>
>>> next(z)
(1, 'a')

Python 中的文件對象也是迭代器。

>>> next(open('hello.txt'))
'hello worldn'

在 Python 標(biāo)準(zhǔn)庫和第三方庫中內(nèi)置了大量的迭代器。這些迭代器首先惰性迭代器一樣,延遲工作直到你請求它們下一項(xiàng)。

創(chuàng)建你自己的迭代器

知道你已經(jīng)在使用迭代器是很有用的,但是我希望你也知道,你可以創(chuàng)建自己的迭代器和你自己的惰性迭代器。

下面這個(gè)類構(gòu)造了一個(gè)迭代器接受一個(gè)可迭代的數(shù)字,并在循環(huán)結(jié)束時(shí)提供每個(gè)數(shù)字的平方。

class square_all:
def __init__(self, numbers):
self.numbers = iter(numbers)
def __next__(self):
return next(self.numbers) * 2
def __iter__(self):
return self

但是在我們開始對該類的實(shí)例進(jìn)行循環(huán)遍歷之前,沒有任何工作要做。

這里,我們有一個(gè)無限長的可迭代對象 count,你可以看到 square_all 接受 count 而不用完全循環(huán)遍歷這個(gè)無限長的迭代:

>>> from itertools import count
>>> numbers = count(5)
>>> squares = square_all(numbers)
>>> next(squares)
25
>>> next(squares)
36

這個(gè)迭代器類是有效的,但我們通常不會這樣做。通常,當(dāng)我們想要做一個(gè)定制的迭代器時(shí),我們會生成一個(gè)生成器函數(shù):

def square_all(numbers):
for n in numbers:
yield n**2

這個(gè)生成器函數(shù)等價(jià)于我們上面所做的類,它的工作原理是一樣的。

這種 yield 語句似乎很神奇,但它非常強(qiáng)大:yield 允許我們在調(diào)用 next 函數(shù)之間暫停生成器函數(shù)。yield 語句是將生成器函數(shù)與常規(guī)函數(shù)分離的東西。

另一種實(shí)現(xiàn)相同迭代器的方法是使用生成器表達(dá)式。

def square_all(numbers):
return (n**2 for n in numbers)

這和我們的生成器函數(shù)確實(shí)是一樣的,但是它使用的語法看起來像是一個(gè)列表推導(dǎo)一樣。如果你需要在代碼中使用惰性迭代,請考慮迭代器,并考慮使用生成器函數(shù)或生成器表達(dá)式。

迭代器如何改進(jìn)你的代碼

一旦你已經(jīng)接受了在代碼中使用惰性迭代器的想法,你就會發(fā)現(xiàn)有很多可能來發(fā)現(xiàn)或創(chuàng)建輔助函數(shù),以此來幫助你循環(huán)遍歷和處理數(shù)據(jù)。

惰性求和

這是一個(gè) for 循環(huán),它對 Django queryset 中的所有工作時(shí)間求和:

hours_worked = 0
for event in events:
if event.is_billable():
hours_worked += event.duration

下面是使用生成器表達(dá)式進(jìn)行惰性評估的代碼:

billable_times = (
event.duration
for event in events
if event.is_billable()
)
hours_worked = sum(billable_times)

請注意,我們代碼的形狀發(fā)生了巨大變化。

將我們的計(jì)算工作時(shí)間變成一個(gè)惰性迭代器允許我們能夠命名以前未命名(billable_times)的東西。這也允許我們使用 sum 函數(shù),我們以前不能使用 sum 函數(shù)是因?yàn)槲覀兩踔翛]有一個(gè)可迭代對象傳遞給它。迭代器允許你從根本上改變你組織代碼的方式。

惰性和打破循環(huán)

這段代碼打印出日志文件的前 10 行:

for i, line in enumerate(log_file):
if i >= 10:
break
print(line)

這段代碼做了同樣的事情,但是我們使用的是 itertools.islice 函數(shù)來惰性地抓取文件中的前 10 行:

from itertools import islice
first_ten_lines = islice(log_file, 10)
for line in first_ten_lines:
print(line)

我們定義的 first_ten_lines 變量是迭代器,同樣,使用迭代器允許我們給以前未命名的東西命名(first_ten_lines)。命名事物可以使我們的代碼更具描述性,更具可讀性。

作為獎(jiǎng)勵(lì),我們還消除了在循環(huán)中使用 break 語句的需要,因?yàn)?islice 實(shí)用函數(shù)為我們處理了中斷。

你可以在標(biāo)準(zhǔn)庫中的 itertools 中找到更多的迭代輔助函數(shù),以及諸如 boltons 和 more-itertools 之類的第三方庫。

創(chuàng)建自己的迭代輔助函數(shù)

你可以在標(biāo)準(zhǔn)庫和第三方庫中找到用于循環(huán)的輔助函數(shù),但你也可以自己創(chuàng)建!

這段代碼列出了序列中連續(xù)值之間的差值列表。

current = readings[0]
for next_item in readings[1:]:
differences.append(next_item - current)
current = next_item

請注意,這段代碼中有一個(gè)額外的變量,我們每次循環(huán)時(shí)都要指定它。還要注意,這段代碼只適用于我們可以切片的東西,比如序列。如果 readings 是一個(gè)生成器,一個(gè) zip 對象或其他任何類型的迭代器,那么這段代碼就會失敗。

讓我們編寫一個(gè)輔助函數(shù)來修復(fù)代碼。

這是一個(gè)生成器函數(shù),它為給定的迭代中的每個(gè)項(xiàng)目提供了當(dāng)前項(xiàng)和下一項(xiàng):

def with_next(iterable):
"""Yield (current, next_item) tuples for each item in iterable."""
iterator = iter(iterable)
current = next(iterator)
for next_item in iterator:
yield current, next_item
current = next_item

我們從可迭代對象中手動獲取一個(gè)迭代器,在它上面調(diào)用 next 來獲取第一項(xiàng),然后循環(huán)遍歷迭代器獲取后續(xù)所有的項(xiàng)目,跟蹤后一個(gè)項(xiàng)目。這個(gè)函數(shù)不僅適用于序列,而且適用于任何類型迭代。

這段代碼和以前代碼是一樣的,但是我們使用的是輔助函數(shù)而不是手動跟蹤 next_item:

differences = []
for current, next_item in with_next(readings):
differences.append(next_item - current)

請注意,這段代碼不會掛在我們循環(huán)周圍的 next_item 上,with_next 生成器函數(shù)處理跟蹤 next_item 的工作。

還要注意,這段代碼已足夠緊湊,如果我們愿意,我們甚至可以將方法復(fù)制到列表推導(dǎo)中來。

differences = [
(next_item - current)
for current, next_item in with_next(readings)
]

再次回顧循環(huán)問題

現(xiàn)在我們準(zhǔn)備回到之前看到的那些奇怪的例子并試著找出到底發(fā)生了什么。

問題 1:耗盡的迭代器

這里我們有一個(gè)生成器對象 squares:

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

如果我們把這個(gè)生成器傳遞給 tuple 構(gòu)造函數(shù),我們將會得到它的一個(gè)元組:

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)
>>> tuple(squares)
(1, 4, 9, 25, 49)

如果我們試著計(jì)算這個(gè)生成器中數(shù)字的和,使用 sum,我們就會得到 0:

>>> sum(squares)
0

這個(gè)生成器現(xiàn)在是空的:我們已經(jīng)把它耗盡了。如果我們試著再次創(chuàng)建一個(gè)元組,我們會得到一個(gè)空元組:

>>> tuple(squares)
()

生成器是迭代器,迭代器是一次性的。它們就像 Hello Kitty Pez 分配器那樣不能重新加載。

問題 2:部分消耗一個(gè)迭代器

再次使用那個(gè)生成器對象 squares:

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

如果我們詢問 9 是否在 squares 生成器中,我們會得到 True:

>>> 9 in squares
True

但是我們再次詢問相同的問題,我們會得到 False:

>>> 9 in squares
False

當(dāng)我們詢問 9 是否在迭代器中時(shí),Python 必須對這個(gè)生成器進(jìn)行循環(huán)遍歷來找到 9。如果我們在檢查了 9 之后繼續(xù)循環(huán)遍歷,我們只會得到最后兩個(gè)數(shù)字,因?yàn)槲覀円呀?jīng)在找到 9 之前消耗了這些數(shù)字:

>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)
>>> 9 in squares
True
>>> list(squares)
[25, 49]

詢問迭代器中是否包含某些東西將會部分地消耗迭代器。如果沒有循環(huán)遍歷迭代器,那么是沒有辦法知道某個(gè)東西是否在迭代器中。

問題 3:拆包是迭代

當(dāng)你在字典上循環(huán)時(shí),你會得到鍵:

>>> counts = {'apples': 2, 'oranges': 1}
>>> for key in counts:
... print(key)
...
apples
oranges

當(dāng)你對一個(gè)字典進(jìn)行拆包時(shí),你也會得到鍵:

>>> x, y = counts
>>> x, y
('apples', 'oranges')

循環(huán)依賴于迭代器協(xié)議,可迭代對象拆包也依賴于有迭代器協(xié)議。拆包一個(gè)字典與在字典上循環(huán)遍歷是一樣的,兩者都使用迭代器協(xié)議,所以在這兩種情況下都得到相同的結(jié)果。

回顧

序列是迭代器,但是不是所有的迭代器都是序列。當(dāng)有人說“迭代器”這個(gè)詞時(shí),你只能假設(shè)他們的意思是“你可以迭代的東西”。不要假設(shè)迭代器可以被循環(huán)遍歷兩次、詢問它們的長度或者索引。

迭代器是 Python 中最基本的可迭代形式。如果你想在代碼中做一個(gè)惰性迭代,請考慮迭代器,并考慮使用生成器函數(shù)或生成器表達(dá)式。

最后,請記住,Python 中的每一種迭代都依賴于迭代器協(xié)議,因此理解迭代器協(xié)議是理解 Python 中的循環(huán)的關(guān)鍵。

分享到:
標(biāo)簽:循環(huán) python
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定