亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python進(jìn)階之迭代器與迭代器切片教程

 更新時(shí)間:2020年01月29日 22:22:35   作者:豌豆花下貓  
迭代器是 Python 中獨(dú)特的一種高級(jí)特性,而切片也是一種高級(jí)特性,兩者相結(jié)合,會(huì)產(chǎn)生什么樣的結(jié)果呢,需要的朋友可以參考下

在前兩篇關(guān)于 Python 切片的文章中,我們學(xué)習(xí)了切片的基礎(chǔ)用法、高級(jí)用法、使用誤區(qū),以及自定義對(duì)象如何實(shí)現(xiàn)切片用法(相關(guān)鏈接見文末)。本文是切片系列的第三篇,主要內(nèi)容是迭代器切片。

迭代器是 Python 中獨(dú)特的一種高級(jí)特性,而切片也是一種高級(jí)特性,兩者相結(jié)合,會(huì)產(chǎn)生什么樣的結(jié)果呢?

1、迭代與迭代器

首先,有幾個(gè)基本概念要澄清:迭代、可迭代對(duì)象、迭代器。

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

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

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

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

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

那怎么判斷一個(gè)對(duì)象是否可迭代呢?為什么它們是可迭代的呢?怎么讓一個(gè)對(duì)象可迭代呢?

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

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

# 方法1:dir()查看__iter__
dir(2)   # 沒有,略
dir("abc") # 有,略

# 方法2:isinstance()判斷
import collections
isinstance(2, collections.Iterable)   # False
isinstance("abc", collections.Iterable) # True

# 方法3:hasattr()判斷
hasattr(2,"__iter__")   # False
hasattr("abc","__iter__") # True

# 方法4:用iter()查看是否報(bào)錯(cuò)
iter(2)   # 報(bào)錯(cuò):'int' object is not iterable
iter("abc") # <str_iterator at 0x1e2396d8f28>

### PS:判斷是否可迭代,還可以查看是否實(shí)現(xiàn)__getitem__,為方便描述,本文從略。

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

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

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

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

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

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

ob1 = "abc"
ob2 = iter("abc")
ob3 = iter("abc")

# ob1它遍歷
for i in ob1:
  print(i, end = " ")  # a b c
for i in ob1:
  print(i, end = " ")  # a b c
# ob1自遍歷
ob1.__next__() # 報(bào)錯(cuò): 'str' object has no attribute '__next__'

# ob2它遍歷
for i in ob2:
  print(i, end = " ")  # a b c  
for i in ob2:
  print(i, end = " ")  # 無輸出
# ob2自遍歷
ob2.__next__() # 報(bào)錯(cuò):StopIteration

# ob3自遍歷
ob3.__next__() # a
ob3.__next__() # b
ob3.__next__() # c
ob3.__next__() # 報(bào)錯(cuò):StopIteration

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

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

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

2、迭代器切片

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

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

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

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

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

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

hi = "歡迎關(guān)注公眾號(hào):Python貓"
it = iter(hi)

# 普通切片
hi[-7:] # Python貓

# 反例:迭代器切片
it[-7:] # 報(bào)錯(cuò):'str_iterator' object is not subscriptable

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

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

import itertools

# 例1:簡(jiǎn)易迭代器
s = iter("123456789")
for x in itertools.islice(s, 2, 6):
  print(x, end = " ")  # 輸出:3 4 5 6
for 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.b
f = iter(Fib())
for x in itertools.islice(f, 2, 6):
  print(x, end = " ") # 輸出:2 3 5 8
for x in itertools.islice(f, 2, 6):
  print(x, end = " ") # 輸出:34 55 89 144

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

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

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],默認(rèn)步長(zhǎng)是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() 方法的索引方向是受限的,但它也提供了一種可能性:即允許你對(duì)一個(gè)無窮的(在系統(tǒng)支持范圍內(nèi))迭代器進(jìn)行切片的能力。這是迭代器切片最具想象力的用途場(chǎng)景。

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

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

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

# test.txt 文件內(nèi)容
'''
貓
Python貓
python is a cat.
this is the end.
'''

from itertools import islice
with 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())
### 輸出結(jié)果:
True
python is a cat.
this is the end.

3、小結(jié)

好啦,今天的學(xué)習(xí)就到這,小結(jié)一下:迭代器是一種特殊的可迭代對(duì)象,可用于它遍歷與自遍歷,但遍歷過程是損耗型的,不具備循環(huán)復(fù)用性,因此,迭代器本身不支持切片操作;通過借助 itertools 模塊,我們能實(shí)現(xiàn)迭代器切片,將兩者的優(yōu)勢(shì)相結(jié)合,其主要用途在于截取大型迭代器(如無限數(shù)列、超大文件等等)的片段,實(shí)現(xiàn)精準(zhǔn)的處理,從而大大地提升性能與效率。

相關(guān)文章

  • Python 獲取當(dāng)前路徑3種方法

    Python 獲取當(dāng)前路徑3種方法

    本文主要介紹了Python 獲取當(dāng)前路徑3種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Python遍歷目錄中的所有文件的方法

    Python遍歷目錄中的所有文件的方法

    Pyhton中我們一般使用os.walk生成器來獲取文件夾中的所有文件,這里我們就來詳細(xì)看一下Python遍歷目錄中的所有文件的方法,包括一個(gè)進(jìn)階的利用fnmatch模塊進(jìn)行匹配的方法:
    2016-07-07
  • 在ironpython中利用裝飾器執(zhí)行SQL操作的例子

    在ironpython中利用裝飾器執(zhí)行SQL操作的例子

    這篇文章主要介紹了在ironpython中利用裝飾器執(zhí)行SQL操作的例子,文章中以操作MySQL為例,需要的朋友可以參考下
    2015-05-05
  • python實(shí)現(xiàn)泊松圖像融合

    python實(shí)現(xiàn)泊松圖像融合

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)泊松圖像融合,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • python使用celery實(shí)現(xiàn)異步任務(wù)執(zhí)行的例子

    python使用celery實(shí)現(xiàn)異步任務(wù)執(zhí)行的例子

    今天小編就為大家分享一篇python使用celery實(shí)現(xiàn)異步任務(wù)執(zhí)行的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08
  • Python 中省略號(hào)對(duì)象的使用

    Python 中省略號(hào)對(duì)象的使用

    作為一名經(jīng)驗(yàn)豐富的 Python 開發(fā)人員,您可能遇到過 Python 中的三個(gè)點(diǎn)…省略號(hào)對(duì)象, 如果您嘗試在 Python 解釋器中打印此內(nèi)容,它將顯示一個(gè)省略號(hào),本篇文章將介紹 Python 中省略號(hào)對(duì)象的使用,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • 使用python進(jìn)行廣告點(diǎn)擊率的預(yù)測(cè)的實(shí)現(xiàn)

    使用python進(jìn)行廣告點(diǎn)擊率的預(yù)測(cè)的實(shí)現(xiàn)

    這篇文章主要介紹了使用python進(jìn)行廣告點(diǎn)擊率的預(yù)測(cè)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 關(guān)于AnacondaNavigator?Jupyter?Notebook更換Python內(nèi)核的問題

    關(guān)于AnacondaNavigator?Jupyter?Notebook更換Python內(nèi)核的問題

    因?yàn)樾掳惭b的Anaconda?Navigator默認(rèn)安裝了一個(gè)Python,Jupyter?Notebook默認(rèn)使用的內(nèi)核就是這個(gè)Python,跟我系統(tǒng)安裝好的Python沖突了,下面小編給大家介紹AnacondaNavigator?Jupyter?Notebook更換Python內(nèi)核的問題,需要的朋友可以參考下
    2022-02-02
  • python flask搭建web應(yīng)用教程

    python flask搭建web應(yīng)用教程

    今天小編就為大家分享一篇python flask搭建web應(yīng)用教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • python怎么刪除緩存文件

    python怎么刪除緩存文件

    在本篇文章里小編給大家整理的是一篇關(guān)于python刪除緩存文件方法,需要的朋友們可以學(xué)習(xí)下。
    2020-07-07

最新評(píng)論