python3基礎 之 迭代器與生成器,生成式

ckxllf發表於2020-04-14

    在python3中可以為序列建立迭代器。迭代器是一個用於記錄在序列中當前遍歷位置的結構,持續訪問它將能依次訪問序列的所有元素。它從序列的第一個元素開始訪問,直到訪問完所有元素。迭代器只能前進不能後退,也即訪問過的元素不能再次訪問。常用的方法有iter()用於建立迭代器,next()函式返回當前元素並指向下一個元素。

  程式碼示例:

  Tuple = (1, 2, 3)

  Iterator1 = iter(Tuple)

  print(next(Iterator1))

  print(next(Iterator1))

  print(next(Iterator1))

  print("-------------------------")

  List = [1, 2, 3]

  Iterator2 = iter(List) #為列表建立一個迭代器

  print(next(Iterator2))

  print(next(Iterator2))

  print(next(Iterator2))

  print("-------------------------")

  Set = {1, 2, 3}

  Iterator3 = iter(Set) #為集合建立迭代器

  print(next(Iterator3))

  print(next(Iterator3))

  print(next(Iterator3))

  print("-------------------------")

  Dictionary = {"1":1, "2":2, "3":3}

  Iterator4 = iter(Dictionary) #為字典建立迭代器

  print(next(Iterator4))

  print(next(Iterator4))

  print(next(Iterator4))

  print("-------------------------")

  string = "123"

  Iterator5 = iter(string) #為字串建立迭代器

  print(next(Iterator5))

  print(next(Iterator5))

  print(next(Iterator5))

  執行結果:

  1

  2

  3

  -------------------------

  1

  2

  3

  -------------------------

  1

  2

  3

  -------------------------

  1

  2

  3

  -------------------------

  1

  2

  3

  Process finished with exit code 0

    可以為字串、元組、列表、集合、字典建立迭代器,也可以把一個類作為迭代器,或者說它們是“可以迭代的”,下面要講的生成器也是可以迭代的。迭代器也可和for迴圈配合使用。

  程式碼示例:

  Tuple = (1, 2, 3)

  Iterator1 = iter(Tuple)

  for val in Iterator1:

  print(val)

  執行結果:

  1

  2

  3

  2 生成式

    講生成器之前還需要補充一下python3中一個很強的特性即“生成式”。在建立列表時,允許將規則寫在列表中,python3將會按照此規則自動推匯出列表的元素。這無疑是一種建立列表的簡潔寫法。

  2.1 為什麼需要“生成式”?

    假設讓你建立這樣一個列表,他的元素符合公式y=x2+xy=x^2+xy=x2+x,x為自然數。如果告訴你只需要前五項,你可以很輕鬆的建立出這個列表

  List = [0, 2, 6, 12, 20]

    如果我要求寫出前10項呢?當然也能寫出,不過這次要廢一點功夫去計算。

  List = [0, 2, 6, 12, 20, 30, 42, 56, 72, 90]

    如果是前100項呢?你可能會先建立一個空列表,然後寫一個迴圈函式去計算沒一個元素並將其裝入列表中。程式碼這個樣子:

  List = [] #建立空列表

  n = 100

  for x in range(n):

  List.append(x * x + x) #計算每一個元素將其裝載入列表

  print(List)

    如果在python3中這樣寫將會非常”笨“的寫法。可以利用生成式這樣建立列表:

  List = [x * x + x for x in range(100)]

  print(List)

    我們將公式x2+xx^2+xx2+x直接寫在了列表的[]內,並且使用了for和range()函式限定了x的取值範圍。這樣python3就可以根據這些規則去自動推匯出列表中的每一個元素。你可以將[]內的程式碼分成兩部分來看,一部分是生成規則x * x + x,另一部分是迴圈結構的範圍限定for x in range(100)。

    列表生成式主要作用就是使得建立列表變得更簡便。

    再思考一個問題,如果要求建立的列表元素包含前一千,甚至一萬項呢?當元素個數龐大的時候,會帶來另外一個問題,就是記憶體開銷過大。這時候我們可以犧牲一點時間效率去節省空間效率。讓列表只記錄計算規則(公式),當需要訪問元素時實時計算生成,不需要儲存所有元素,這就是後面要講到的生成器的作用。

  2.2 列表生成式語法規則

    活用生成式將會使得程式碼更簡潔有效。

  2.2.1 生成式給出規則和範圍

    上面我們已經看到了列表生成式中給出生成規則(公式)和引數範圍的語法結構了。再回顧一下:List = [x * x + x for x in range(100)]

    接下來介紹更復雜一點的用法以滿足更多的設計需求。

  2.2.2 for語句之後加入if語句

    對於上面的列表我們新增一個規則,就是元素既要符合公式y=x2+xy=x^2+xy=x2+x,x為自然數,而且要求元素必須被能5整除。為了滿足這個要求我們需要對元素進行 篩選。使用if語句進行篩選。

  程式碼示例:

  List = [x * x + x for x in range(100) if x % 5 == 0]

  print(List)

  執行結果:

  [0, 30, 110, 240, 420, 650, 930, 1260, 1640, 2070, 2550, 3080, 3660, 4290, 4970, 5700, 6480, 7310, 8190, 9120]

    篩選後100個元素只剩下20個了。只是在[]裡for語句之後新增了一句if x % == 0就對元素進行了篩選,只留下符合條件的元素。

    也可以在for語句之後使用多個if,設定多個篩選條件。

    下面的程式碼篩選條件為能被5和2整除的元素。

  程式碼示例:

  List1 = [x * x + x for x in range(100) if x % 5 == 0 if x % 2 == 0] #for語句後面使用兩個if,篩選同時滿足兩個if的條件

  print(List1)

  List2 = [x * x + x for x in range(100) if x % 5 == 0 and x % 2 == 0] #for後面使用兩個if,篩選同時滿足兩個if的條件

  print(List2)

  執行結果:

  [0, 110, 420, 930, 1640, 2550, 3660, 4970, 6480, 8190]

  [0, 110, 420, 930, 1640, 2550, 3660, 4970, 6480, 8190]

    生成式中for語句之後加入if語句事先對元素的篩選,需要 注意 的是不能使用if-else、if-elif、if-elif-else這些結構,只能使用if結構。

  2.2.3 for語句之前加入if語句

    for語句前面使用if-else語句,它其實是生成規則(公式)的一部分,你可以簡單理解為分段函式。要注意只能使用if-else結構。

    我們對上面的例子我們再增加一個條件,前100個元素中,前50個元素需要滿足公式y=x^2+x,後50個需要滿足公式y=-x^2+x。這是一個分段函式,根據x的奇偶性去決定使用哪個公式,也即有一個選擇的動作,這就需要if-else去實現。

  程式碼示例:

  List = [x * x + x if x < 50 else -(x * x) + x for x in range(100) if x % 5 == 0 and x % 2 == 0] #for後面使用兩個if,篩選同時滿足兩個if的條件

  print(List)

  執行結果:

  [0, 110, 420, 930, 1640, -2450, -3540, -4830, -6320, -8010]

    上面的程式碼當滿足if x % 2 == 0

  2.2.4 多個引數

    上面我們介紹的生成式都是一個引數即y=f(x)y=f(x)y=f(x)的形式,實際上是可以多個引數的,這裡我們以兩個引數舉例。兩個引數的生成式需要滿足公式z=f(x,y)z=f(x,y)z=f(x,y)。我們假設需要建立這樣一個序列:x引數是奇數,y引數是偶數。計算5以內的x與y的和。需要滿足公式z=x+yz=x+yz=x+y且x∈[1, 3, 5],y∈[0, 2, 4]。

  程式碼示例:

  a = [1, 3, 5]

  b = [0, 2, 4]

  List = [x + y for x, y in zip(a, b)] #zip函式是python內建函式,將元組a和b打包為元素是列表的元組

  print(List)

    簡單解釋下zip()函式,它是python的內建函式,在這裡它的作用合併列表a和b生成列表[(1, 0), (3, 2), (5, 4)]。x和y的值就取自這個新元組中的元素,從而實現了兩個取值不同的引數求和。三個及以上的引數可以類推。

  [1, 5, 9]

  2.2.4 多個for

    多個for主要用於將多個引數的取值進行一一組合。比如兩個字串"ABC"和"abc"它們的字元之間有哪些組合?

  str1 = "ABC"

  str2 = "abc"

  List = [x + y for x in str1 for y in str2] #兩個for

  print(List)

    字串相加就是把字串拼接。兩個for分別遍歷了x和y的所有可能取值,則總的元素可能就是兩個for遍歷元素的組合。三個及以上個數的for可以類推。

    透過上面的學習你可能也驚歎這麼複雜的邏輯都可以在列表定義時就極其精簡的實現了,這確實是python強大的一個地方。

  3 生成器

  3.1 使用()建立生成器

    迭代器和可迭代的資料型別將所有元素都放在記憶體,當需要時進行訪問讀取,如果元素個數龐大將會非常消耗記憶體。這時候可以犧牲一點時間節省空間,我們使用生成器,它記錄的不是每個元素的值,而是生成元素的規則,當進行訪問時透過規則實時計算出來。由於生成器也是可迭代的,所以和迭代器一樣可以使用next()、和for迴圈進行迭代。

    下面程式碼實現將列表元素用生成器產生,構建了一個生成器。

  程式碼示例:

  List1 = [x * x + x for x in range(5)] #使用生成式建立一個列表

  List2 = (x * x + x for x in range(5)) #建立一個生成器,將[]改為()

  print(List1)

  print(List2) #這是一個生成器

  print("-------------------------")

  Iterator1 = iter(List1) #為列表建立一個迭代器

  print(next(Iterator1)) #迭代器可適用next()函式迭代

  print(next(Iterator1))

  print(next(Iterator1))

  print("-------------------------")

  print(next(List2)) #生成器也是可迭代的,可以使用next(0函式迭代

  print(next(List2))

  print(next(List2))

  print("-------------------------")

  for val in Iterator1: #迭代器可適用for迴圈進行迭代,繼續往後迭代

  print(val)

  print("-------------------------")

  for val in List2: #生成器可適用for迴圈進行迭代

  print(val)

  執行結果:

  [0, 2, 6, 12, 20]

  at 0x0000020FC495BE40>

  -------------------------

  0

  2

  6

  -------------------------

  0

  2

  6

  -------------------------

  12

  20

  -------------------------

  12

  20

  Process finished with exit code 0

    生成器也是可迭代的,所以可使用next()函式進行迭代,也可使用for迴圈進行迭代。也有很多人將生成器看做一種特殊的迭代器。

  生成器和迭代器共同點:

    都能記錄元素的當前訪問位置,都可被用next()和for進行迭代,迭代只能向前不能退後。

  生成器和迭代器不同點:

    在建立上迭代器使用iter()而生成器使用()或yield建立。在迭代的過程中迭代器只是把事先儲存的值返回,而生成器需要按照規則去計算後返回。

  3.2 使用yield建立生成器

    yield被用於函式中呼叫,使用了該關鍵字的函式就成了生成器。生成器函式與普通函式不同,它返回的是迭代器。yield有點類似return,它們能退出函式並返回一直值。不同的是return不會記錄函式的資訊,而使用yield會記錄下函式當前執行的所有資訊後退出函式並返回一個值,待下次再次呼叫這個函式時,繼續使用儲存的資訊並從上次退出的yield位置開始執行。通俗點說迭代器就是實現了一個“延遲”功能,它可以將函式的執行暫停。包括前面講的()建立的迭代器也是同樣的思想,不需要一次性將所有結果計算出來儲存,而是當需要的時候進行計算並給出結果,最大目的就是為了節省空間。

  程式碼示例: 鄭州人流醫院哪家好

  def IteratorFunction():

  a = 0 #區域性變數

  a +=1

  yield a #執行到yield時儲存當前執行的資訊(比如變數a的值1),然後返回a的值並退出函式。

  a +=1

  yield a

  a +=1

  yield a

  IF1 = IteratorFunction() #建立一個迭代器

  IF2 = IteratorFunction() #建立第二個個迭代器

  print(next(IF1)) #執行到yield時返回值1並記錄函式狀態後退出

  print(next(IF1)) #再次呼叫從上一次yield位置繼續執行

  print(next(IF2)) #IF1和IF是兩個獨立的迭代器

  '''

  #也可以呼叫用__next__()進行迭代

  print(IF1.__next__())

  print(IF1.__next__())

  print(IF2.__next__())

  '''

  執行結果:

  1

  2

  1

    這個專題還沒有講到函式,這裡稍微有點“超綱”涉及到函式了。

    以上在生成器函式里放置了三個yield,可以理解為三個“斷電”。經過多次呼叫和返回區域性變數並沒有被釋放。生成器函式IteratorFunction並不能直接用於迭代,而是透過呼叫它後返回迭代器的特性,建立了TF1和TF2兩個獨立的迭代器,也即能用於迭代的是TF1和TF2。反覆呼叫TF1並不會改變TF2的執行資訊。

    放置了三個yield,呼叫三次會分別停留在三個yield上,如果呼叫四次及以上會發生什麼?答案是會丟擲StopIteration異常,因為迭代器已經迭代到邊界了,也就是執行到return了,雖然我們省略了它。yield只是暫停return是真正的結束也意味著迭代到達邊界。一般我們會在生成器函式里放上迴圈來使用。

    下面這個例子用生成器實現了斐波那契數列的迭代。

  def Fibonacci_Iter():

  a, b = 1, 1 #斐波那契前兩項元素

  yield 1 #第一個元素

  yield 1 #第二個元素

  while(True):

  a, b = b, a + b

  yield b #開始元素迭代

  fb = Fibonacci_Iter()

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  運算結果:

  1

  1

  2

  3

  5

  8

  13

    以上例子為了簡單化和說明作用,我將while迴圈寫成了死迴圈,也即這個迭代器是沒有結束”邊界“的,現實計算機不能支援無限次呼叫的運算量,這是不好的示範,是為了提醒大家不要只關注迭代器迭代推導的規則,還需要關注邊界。

  重構這段程式碼:

  def Fibonacci_Iter():

  a, b = 1, 1 #斐波那契前兩項元素

  IterNum = 0 #迭代次數

  while(IterNum < 5):

  IterNum += 1 #迭代次數累加

  if IterNum <= 2:

  yield 1

  else:

  a, b = b, a + b

  yield b #開始元素迭代

  return -1 #執行到return就代表迭代到邊界了,-1將隨異常丟擲

  fb = Fibonacci_Iter()

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  print(next(fb))

  執行結果:

  1

  1

  2

  3

  5

  Traceback (most recent call last):

  File "C:/Users/think/Desktop/Python_Test/Test.py", line 20, in

  print(next(fb))

  StopIteration: -1

    可以看到我們限定了迭代次數為5次,當第六次迭代時由於執行到return迭代到達邊界停止,丟擲了異常StopIteration: -1,其中-1既是我們return的值。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2686073/,如需轉載,請註明出處,否則將追究法律責任。

相關文章