乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      python入門系列:迭代器和生成器

       xiaoyimin 2019-02-13

      python的迭代協(xié)議

      引言

      迭代器是訪問集合內(nèi)部元素的一種方式,一般用來遍歷數(shù)據(jù)。

      迭代器和用下標索引訪問的方式不一樣,迭代器是不能直接返回值的。

      迭代器提供了一種惰性訪問數(shù)據(jù)的方式,需要的時候才產(chǎn)生數(shù)據(jù)。

      可迭代類型都實現(xiàn)了迭代協(xié)議,實際上就是__iter__()這個魔法函數(shù)。

      可迭代類型和迭代器

      前面講過,collections.abc模塊中定義了很多內(nèi)置的抽象基類,現(xiàn)在我們重點關注其中的兩個:Iterable 和 Iterator

      Iterable

      里面定義了一個抽象方法,__iter__(),也就是說某個類只要實現(xiàn)了這個魔法函數(shù),它就是可迭代的類型

      Iterator

      首先,Iterator繼承了Iterable,在它的基礎上,又增加了一個抽象方法:__next__(),它是用來讓迭代器獲取下一個元素。

      小結

      可迭代類型和迭代器并不一樣,前者只需要實現(xiàn)__iter__()函數(shù),而對后者而言,__next__()才是它的核心。

      比如list類型,它是一個可迭代類型,但并不是一個迭代器。a = [1, 2, 3]

      print(isinstance(a, Iterable), isinstance(a, Iterator))

      # result:

      # True False

      補充

      在魔法函數(shù)那一小節(jié),我們講過這樣一個例子:class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      def __getitem__(self, item):

      return self.lans[item]

      language = Language(['Python', 'C', 'Lisp'])

      for lan in language:

      print(lan)

      # result:

      # Python

      # C

      # Lisp

      在Language這個類中,我們定義的是__getitem__這個魔法函數(shù),然后對這個類產(chǎn)生的實例我們可以使用for來遍歷元素了。也就是說它成為了一個可迭代類型,但它并沒有實現(xiàn)剛才我們討論的__iter__()函數(shù)。

      這是因為,在Python內(nèi)部,很多地方做了兼容處理,當我們是用for進行迭代遍歷,解釋器首先會尋找__iter__()函數(shù),如果沒有,它就會退一步去尋找__getitem__(),這個是序列類型中會實現(xiàn)的一個魔法函數(shù),只要它接收從 0 開始的整數(shù)為參數(shù),這個對象也是會被當做可迭代類型的。

      實際上,僅僅滿足了可迭代類型還不夠,真正能進行迭代取值的是迭代器。通過iter()函數(shù),我們可以返回一個可迭代對象的迭代器,有了它才能進行迭代取值。class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      def __getitem__(self, item):

      return self.lans[item]

      language = Language(['Python', 'C', 'Lisp'])

      my_iterator = iter(language)

      print(my_iterator)

      # result:

      #

      如果我們不實現(xiàn)__iter__()或__getitem__(),獲取迭代器的過中會出錯class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      language = Language(['Python', 'C', 'Lisp'])

      my_iterator = iter(language)

      print(my_iterator)

      # result:

      # TypeError: 'Language' object is not iterable

      有了迭代器,迭代取值需要另外一個函數(shù)next(),每調(diào)用一次,就會返回一個值,直到拋出一個迭代結束的異常。class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      def __getitem__(self, item):

      return self.lans[item]

      language = Language(['Python', 'C', 'Lisp'])

      my_iterator = iter(language)

      print(next(my_iterator))

      print(next(my_iterator))

      print(next(my_iterator))

      print(next(my_iterator))

      # result:

      # Python

      # C

      # Lisp

      # StopIteration

      上面的結果已經(jīng)很接近直接使用for進行迭代了,但是,依賴__getitem__()函數(shù),底層還是隱藏了很多細節(jié),如果我們想純粹地通過__iter__()來實現(xiàn)迭代過程,要怎么做呢?

      __iter__()用來返回一個迭代器,通過這個迭代器來迭代取值,對應顯示調(diào)用iter()的邏輯。

      __next__()用來讓迭代器取下一個值,對應顯示調(diào)用next()的邏輯。

      使用for的時候,這兩個魔法函數(shù)會被自動調(diào)用,完成迭代取值過程。from collections.abc import Iterator

      class MyIterator(Iterator):

      def __init__(self, data_list):

      self.iter_list = data_list

      self.index = 0

      def __next__(self):

      # 這里是通過記錄索引,單次取值達到迭代目的

      # 更好的方式是通過生成器來進行迭代取值

      try:

      data = self.iter_list[self.index]

      except IndexError:

      raise StopIteration

      self.index += 1

      return data

      class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      def __iter__(self):

      return MyIterator(self.lans)

      language = Language(['Python', 'C', 'Lisp'])

      for lan in language:

      print(lan)

      # result:

      # Python

      # C

      # Lisp

      生成器函數(shù)使用

      引言

      函數(shù)里面只要存在yield關鍵字,它就是生成器函數(shù)

      生成器是惰性計算的一個關鍵

      使用案例def gen_func():

      yield 'MetaTian'

      def func():

      return 'MetaTian'

      gen, res = gen_func(), func()

      print(gen)

      print(res)

      # result:

      #

      # MetaTian

      for val in gen:

      print(val)

      # result:

      # MetaTian

      第一個函數(shù)返回的是一個生成器對象,它是一個可迭代類型,因此,可以通過for進行訪問。def gen_func():

      yield 1

      yield 2

      yield 3

      gen = gen_func()

      for val in gen:

      print(val)

      # result:

      # 1

      # 2

      # 3

      生成器的原理

      Python中函數(shù)工作原理

      對于編譯型語言,函數(shù)的調(diào)用會維持一個函數(shù)調(diào)用棧,某個函數(shù)執(zhí)行完成后,它就會被出棧處理,也就是說,函數(shù)執(zhí)行后,它的生命周期就結束了。

      對于Python這樣的解釋型語言,函數(shù)的調(diào)用也要依賴棧結構,但是,函數(shù)對象是存放在堆內(nèi)存中的,也就意味著,一個函數(shù)被調(diào)用執(zhí)行了,它還在那兒。

      什么是堆內(nèi)存和棧內(nèi)存?

      解釋器用一個叫做PyEval_EvalFrameEx的C函數(shù)來執(zhí)行Python程序。對于一個Python中的函數(shù),解釋器接受一個棧幀(stack frame)對象,并在這個棧幀的上下文中執(zhí)行Python字節(jié)碼,完成函數(shù)調(diào)用。在字節(jié)碼執(zhí)行中,如果遇到了要調(diào)用其他函數(shù)的指令,解釋器會創(chuàng)建一個新的棧幀用來執(zhí)行新調(diào)用的函數(shù)。

      生成器+函數(shù)def gen_func():

      yield 1

      name = 'MetaTian'

      yield 2

      return 'done'

      在Python將函數(shù)編譯為字節(jié)碼時,如果遇到y(tǒng)ield關鍵字,它就知道這是一個生成器函數(shù),內(nèi)部會做一個標記。當我們調(diào)用這個函數(shù)的時候,解釋器看到這個標記后就會創(chuàng)建一個生成器,而不是去運行它,后續(xù)函數(shù)的執(zhí)行交給生成器控制。

      這個生成器內(nèi)部有兩個東西,一是對棧幀的引用,二是函數(shù)字節(jié)碼的引用。棧幀中有一個指針,指向最近執(zhí)行的那條指令,因為執(zhí)行到和yield有關的字節(jié)碼后,函數(shù)會停止執(zhí)行,相當于打了個斷點,同時將yield后面的值返回。通過next(),可以讓函數(shù)繼續(xù)執(zhí)行(因為生成器也是迭代器),直到遇到下一個yield。

      生成器對象也是分配在堆內(nèi)存中的,也就是說,只要我們在程序運行的任何地方拿到了這個對象,都可以用它來控制函數(shù)的執(zhí)行。這也是后面攜程的一個理論基礎。重構自己的可迭代類型

      引言

      前面將Language這個類,構建成為了我們自定義的一個可迭代類型。

      生成器也是迭代器,通過使用生成器,可以更簡潔地達成目的。from collections.abc import Iterator

      def gen_func():

      yield 'MetaTian'

      gen = gen_func()

      print(isinstance(gen, Iterator))

      # result:

      # True

      使用案例class Language(object):

      def __init__(self, language_list):

      self.lans = language_list

      def __iter__(self):

      i = 0

      try:

      while True:

      val = self.lans[i]

      yield val

      i += 1

      except IndexError:

      return

      language = Language(['Python', 'C', 'Lisp'])

      for lan in language:

      print(lan)

      # result:

      # Python

      # C

      # Lisp

      總結

      這里再來回顧一下前面講過的內(nèi)容,使用for遍歷的時候,首先會看作用對象是否為一個可迭代類型,如果是,那么會隱式調(diào)用__iter__(),得到一個迭代器對象,有了它,再隱式調(diào)用__next__()來不斷獲取下一個元素,直到

      遇到一個停止迭代的異常。我們也可以通過內(nèi)置的兩個函數(shù)iter()和next()來人工干預迭代的過程。

      在Language類中,__iter__()內(nèi)部加入了一個生成器的邏輯,結合前面的生成器函數(shù),可以知道,遇到y(tǒng)ield語句后,會產(chǎn)生一個生成器對象,由它來控制這個函數(shù)的后續(xù)執(zhí)行。

      因為生成器也是迭代器,所以__next__()的邏輯對它同樣適用,每次 next 都會在__iter__()函數(shù)中的while循環(huán)中不斷取值,直到拋出一個IndexError,迭代結束,__iter__()函數(shù)結束,for邏輯完成。生成器讀取大文件

      引言

      有一個數(shù)據(jù)文件,大小為 10GB

      數(shù)據(jù)只有一行,行中有特殊的分隔符,現(xiàn)在需要剔除分隔符,獲得每一個被分隔的元素。比如,數(shù)據(jù)文件長這樣:

      dj134o0kgfdkjfkdjfk'6823sdkfslkfsldkfj'sdkfslfjyerojskfj...

      是其中的分隔符

      實現(xiàn)過程def extract(f, sep):

      buff = ''

      while True:

      block = f.read(1024*4)

      if not block: # 沒讀到內(nèi)容,說明讀到尾部了

      yield buff # 上次留下來的內(nèi)容

      break

      buff += block

      while sep in buff:

      cut = buff.index(sep) # 定位

      yield buff[:cut] # 分隔符前的一個元素

      buff = buff[cut + len(sep)] # 跳過分隔符和前面的元素

      with open('data.txt') as f:

      for line in extract(f, ''):

      print(line)

      喜歡python + qun:839383765 可以獲取Python各類免費最新入門學習資料!

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多