分析Python迭代器與迭代器切片-創(chuàng)新互聯(lián)

這篇文章主要講解了“分析Python迭代器與迭代器切片”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“分析Python迭代器與迭代器切片”吧!

成都創(chuàng)新互聯(lián)-專業(yè)網站定制、快速模板網站建設、高性價比邕寧網站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式邕寧網站制作公司更省心,省錢,快速模板網站建設找我們,業(yè)務覆蓋邕寧地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。

1、迭代與迭代器

首先,有幾個基本概念要澄清:迭代、可迭代對象、迭代器。

迭代 是一種遍歷容器類型對象(例如字符串、列表、字典等等)的方式,例如,我們說迭代一個字符串“abc”,指的就是從左往右依次地、逐個地取出它的全部字符的過程。(PS:漢語中迭代一詞有循環(huán)反復、層層遞進的意思,但 Python 中此詞要理解成單向水平線性 的,如果你不熟悉它,我建議直接將其理解為遍歷。)

那么,怎么寫出迭代操作的指令呢?最通用的書寫語法就是 for 循環(huán)。

# for循環(huán)實現(xiàn)迭代過程for char in "abc":  print(char, end=" ")# 輸出結果:a b c

for 循環(huán)可以實現(xiàn)迭代的過程,但是,并非所有對象都可以用于 for 循環(huán),例如,上例中若將字符串“abc”換成任意整型數(shù)字,則會報錯: 'int' object is not iterable .

這句報錯中的單詞“iterable”指的是“可迭代的”,即 int 類型不是可迭代的。而字符串(string)類型是可迭代的,同樣地,列表、元組、字典等類型,都是可迭代的。

那怎么判斷一個對象是否可迭代呢?為什么它們是可迭代的呢?怎么讓一個對象可迭代呢?

要使一個對象可迭代,就要實現(xiàn)可迭代協(xié)議,即要實現(xiàn)__iter__()魔術方法,換言之,只要實現(xiàn)了這個魔術方法的對象都是可迭代對象。

那怎么判斷一個對象是否實現(xiàn)了這個方法呢?除了上述的for循環(huán)外,我知道四種方法:

# 方法1:dir()查看__iter__dir(2)   # 沒有,略dir("abc") # 有,略# 方法2:isinstance()判斷import collectionsisinstance(2, collections.Iterable)   # Falseisinstance("abc", collections.Iterable) # True# 方法3:hasattr()判斷hasattr(2,"__iter__")   # Falsehasattr("abc","__iter__") # True# 方法4:用iter()查看是否報錯iter(2)   # 報錯:'int' object is not iterableiter("abc") # <str_iterator at 0x1e2396d8f28>### PS:判斷是否可迭代,還可以查看是否實現(xiàn)__getitem__,為方便描述,本文從略。

這幾種方法中最值得一提的是 iter() 方法,它是 Python 的內置方法,其作用是將可迭代對象變成迭代器 。這句話可以解析出兩層意思:(1)可迭代對象跟迭代器是兩種東西;(2)可迭代對象能變成迭代器。

實際上,迭代器必然是可迭代對象,但可迭代對象不一定是迭代器。兩者有多大的區(qū)別呢?

如上圖藍圈所示,普通可迭代對象與迭代器的最關鍵區(qū)別可概括為:一同兩不同 ,所謂“一同”,即兩者都是可迭代的(__iter__),所謂“兩不同”,即可迭代對象在轉化為迭代器后,它會丟失一些屬性(__getitem__),同時也增加一些屬性(__next__)。

首先看看增加的屬性__next__ , 它是迭代器之所以是迭代器的關鍵,事實上,我們正是把同時實現(xiàn)了__iter__ 方法 和__next__ 方法的對象定義為迭代器的。

有了多出來的這個屬性,可迭代對象不需要借助外部的 for 循環(huán)語法,就能實現(xiàn)自我的迭代/遍歷過程。我發(fā)明了兩個概念來描述這兩種遍歷過程(PS:為了易理解,這里稱遍歷,實際也可稱為迭代):它遍歷 指的是通過外部語法而實現(xiàn)的遍歷,自遍歷 指的是通過自身方法實現(xiàn)的遍歷。

借助這兩個概念,我們說,可迭代對象就是能被“它遍歷”的對象,而迭代器是在此基礎上,還能做到“自遍歷”的對象。

ob1 = "abc"ob2 = iter("abc")ob3 = iter("abc")# ob1它遍歷for i in ob1:  print(i, end = " ")  # a b cfor i in ob1:  print(i, end = " ")  # a b c# ob1自遍歷ob1.__next__() # 報錯: 'str' object has no attribute '__next__'# ob2它遍歷for i in ob2:  print(i, end = " ")  # a b cfor i in ob2:  print(i, end = " ")  # 無輸出# ob2自遍歷ob2.__next__() # 報錯:StopIteration# ob3自遍歷ob3.__next__() # aob3.__next__() # bob3.__next__() # cob3.__next__() # 報錯:StopIteration

通過上述例子可看出,迭代器的優(yōu)勢在于支持自遍歷,同時,它的特點是單向非循環(huán)的,一旦完成遍歷,再次調用就會報錯。

對此,我想到一個比方:普通可迭代對象就像是子彈匣,它遍歷就是取出子彈,在完成操作后又裝回去,所以可以反復遍歷(即多次調用for循環(huán),返回相同結果);而迭代器就像是裝載了子彈匣且不可拆卸的槍,進行它遍歷或者自遍歷都是發(fā)射子彈,這是消耗性的遍歷,是無法復用的(即遍歷會有盡頭)。

寫了這么多,稍微小結一下:迭代是一種遍歷元素的方式,按照實現(xiàn)方式劃分,有外部迭代與內部迭代兩種,支持外部迭代(它遍歷)的對象就是可迭代對象,而同時還支持內部迭代(自遍歷)的對象就是迭代器;按照消費方式劃分,可分為復用型迭代與一次性迭代,普通可迭代對象是復用型的,而迭代器是一次性的。

2、迭代器切片

前面提到了“一同兩不同”,最后的不同是,普通可迭代對象在轉化成迭代器的過程中會丟失一些屬性,其中關鍵的屬性是__getitem__。在《Python進階:自定義對象實現(xiàn)切片功能》中,我曾介紹了這個魔術方法,并用它實現(xiàn)了自定義對象的切片特性。

那么問題來了:為啥迭代器不繼承這個屬性呢?

首先,迭代器使用的是消耗型的遍歷,這意味著它充滿不確定性,即其長度與索引鍵值對是動態(tài)衰減的,所以很難 get 到它的 item ,也就不再需要__getitem__屬性了。其次,若強行給迭代器加上這個屬性,這并不合理,正所謂強扭的瓜不甜……

由此,新的問題來了:既然會丟失這么重要的屬性(還包括其它未標識的屬性),為什么還要使用迭代器呢?

這個問題的答案在于,迭代器擁有不可替代的強大的有用的功能,使得 Python 要如此設計它。限于篇幅,此處不再展開,后續(xù)我會專門填坑此話題。

還沒完,死纏爛打的問題來了:能否令迭代器擁有這個屬性呢,即令迭代器繼續(xù)支持切片呢?

hi = "歡迎關注公眾號:Python貓"it = iter(hi)# 普通切片hi[-7:] # Python貓# 反例:迭代器切片it[-7:] # 報錯:'str_iterator' object is not subscriptable

迭代器因為缺少__getitem__,因此不能使用普通的切片語法。想要實現(xiàn)切片,無非兩種思路:一是自己造輪子,寫實現(xiàn)的邏輯;二是找到封裝好的輪子。

Python 的 itertools 模塊就是我們要找的輪子,用它提供的方法可輕松實現(xiàn)迭代器切片。

import itertools# 例1:簡易迭代器s = iter("123456789")for x in itertools.islice(s, 2, 6):  print(x, end = " ")  # 輸出:3 4 5 6for x in itertools.islice(s, 2, 6):  print(x, end = " ")  # 輸出:9# 例2:斐波那契數(shù)列迭代器class Fib():  def __init__(self):    self.a, self.b = 1, 1  def __iter__(self):    while True:      yield self.a      self.a, self.b = self.b, self.a + self.bf = iter(Fib())for x in itertools.islice(f, 2, 6):  print(x, end = " ") # 輸出:2 3 5 8for x in itertools.islice(f, 2, 6):  print(x, end = " ") # 輸出:34 55 89 144

itertools 模塊的 islice() 方法將迭代器與切片完美結合,終于回答了前面的問題。然而,迭代器切片跟普通切片相比,前者有很多局限性。首先,這個方法不是“純函數(shù)”(純函數(shù)需遵守“相同輸入得到相同輸出”的原則,之前在《來自Kenneth Reitz大神的建議:避免不必要的面向對象編程》提到過);其次,它只支持正向切片,且不支持負數(shù)索引,這都是由迭代器的損耗性所決定的。

那么,我不禁要問:itertools 模塊的切片方法用了什么實現(xiàn)邏輯呢?下方是官網提供的源碼:

def islice(iterable, *args):  # islice('ABCDEFG', 2) --> A B  # islice('ABCDEFG', 2, 4) --> C D  # islice('ABCDEFG', 2, None) --> C D E F G  # islice('ABCDEFG', 0, None, 2) --> A C E G  s = slice(*args)  # 索引區(qū)間是[0,sys.maxsize],默認步長是1  start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1  it = iter(range(start, stop, step))  try:    nexti = next(it)  except StopIteration:    # Consume *iterable* up to the *start* position.    for i, element in zip(range(start), iterable):      pass    return  try:    for i, element in enumerate(iterable):      if i == nexti:        yield element        nexti = next(it)  except StopIteration:    # Consume to *stop*.    for i, element in zip(range(i + 1, stop), iterable):      pass

islice() 方法的索引方向是受限的,但它也提供了一種可能性:即允許你對一個無窮的(在系統(tǒng)支持范圍內)迭代器進行切片的能力。這是迭代器切片最具想象力的用途場景。

除此之外,迭代器切片還有一個很實在的應用場景:讀取文件對象中給定行數(shù)范圍的數(shù)據(jù)。

在《給Python學習者的文件讀寫指南(含基礎與進階,建議收藏)》里,我介紹了從文件中讀取內容的幾種方法:readline() 比較雞肋,不咋用;read() 適合讀取內容較少的情況,或者是需要一次性處理全部內容的情況;而 readlines() 用的較多,每次迭代讀取內容,既減少內存壓力,又方便逐行對數(shù)據(jù)處理。

雖然 readlines() 有迭代讀取的優(yōu)勢,但它是從頭到尾逐行讀取,若文件有幾千行,而我們只想要讀取少數(shù)特定行(例如第1000-1009行),那它還是效率太低了。考慮到文件對象天然就是迭代器 ,我們可以使用迭代器切片先行截取,然后再處理,如此效率將大大地提升。

# test.txt 文件內容'''貓Python貓python is a cat.this is the end.'''from itertools import islicewith open('test.txt','r',encoding='utf-8') as f:  print(hasattr(f, "__next__")) # 判斷是否迭代器  content = islice(f, 2, 4)  for line in content:    print(line.strip())### 輸出結果:Truepython is a cat.this is the end.

感謝各位的閱讀,以上就是“分析Python迭代器與迭代器切片”的內容了,經過本文的學習后,相信大家對分析Python迭代器與迭代器切片這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián)網站建設公司,,小編將為大家推送更多相關知識點的文章,歡迎關注!

網站名稱:分析Python迭代器與迭代器切片-創(chuàng)新互聯(lián)
文章URL:http://muchs.cn/article32/psdpc.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供網站營銷、標簽優(yōu)化、網站策劃關鍵詞優(yōu)化、建站公司、虛擬主機

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都網站建設