1. 程式人生 > >day14 迭代器和生成器

day14 迭代器和生成器

1.迭代器

  名詞解釋

  什麼是迭代:迭代是一個重複過程,但是每次重複都是基於上一次的結果而繼續的

  #下列迴圈只是單純的重複,沒有意義

  while True:

    print(1)

  #基於索引的迭代取值

  l = ['a'a,'b','c']

  i = 0

  while i <len(l):

    print(l[i])

    i+=1 

  什麼是迭代器:

    迭代取值的工具

2.為什麼要用迭代器

迭代器

  1.優點:提供了一種不依賴索引的迭代取值方式

  2.更節省記憶體(每次只取一個值放到記憶體中使用,下次呼叫才會取下個值)

缺點:

  1.不如按照索引的取值方式靈活

  2.取值一次性的,只能往後取,無法預測值的個數

 

3.如何用迭代器

  可迭代物件:str、list、tuple\dict\set\檔案物件

  但凡有__iter__方法的物件都稱之為可迭代物件

 

  迭代器物件:檔案物件

    既內建有__iter__方法又內建又__next__方法的物件都稱之為迭代器物件

    (關鍵點:呼叫可迭代物件下__iter__方法,會有一個返回值,該返回值就是內建的迭代器物件)

用內建方法__next__ 可以取出迭代器的下一個值

上面iter_d拿到由d的內容產生的迭代器

但是當next取到最後一個值之後,繼續next取值,就會報錯,會丟擲異常 StopIteration

 

d={'k1':111,'k2':222,'k3':333}

d={1,2,3,4,5}

iter_d = d.__iter__()  一般__下滑線開頭的方法,滿足一定條件時自動觸發,最好不要這麼用

可以用iter_d = iter(d) 來用,就相當於d.__iter__

next也一樣

print(next(iter_d)) # 相當於iter_d.__next__()

print(len(d))

就相當於print(d.__len__())

 

print(iter.d.__iter__().__iter__().__iter__() is iter_d)

上面語句返回的是true,也就是說迭代器的iter返回值就是這個迭代器本身,只是python對於可迭代物件和迭代器物件不寫兩種實現方式了,所以都用相同的方法,返回都是迭代器

 

print(d.__iter__().__next__())

print(d.__iter__().__next__())

print(d.__iter__().__next__())

以上三條語句的返回值都一樣,都是d產生的迭代器的第一個元素

 

while True:

  try:

    v = iter_d.next()

    print(v)

   except StopIteration:

    break

print('第二次取值‘)

 

#iter_d = d.__iter__()  # 沒有這句的話,下面的第二次取值不會取到值,因為iter_d這個迭代器已經在上一次迴圈中取到末尾報異常結束了,如果不重置這個迭代器,就會繼續報異常直接結束取值過程

while True:

  try:

    v = iter_d.__next__()

    print(v)

  except StopIteration:

    break

以上用try去捕捉異常的寫法太麻煩了,其實for語句的底層機制就和上面的語句一樣

for k in d:

  print(k)

for迴圈的底層原理

1.呼叫in後面那個值、物件的__iter__方法,拿到一個迭代器物件iter_obj

2.呼叫迭代器物件iter_obj.__next__()將得到的值返回值賦值給變數名k,迴圈往復直到取值完畢丟擲異常StopIteration

3.捕捉異常結束迴圈

 

2.生成器

什麼是生成器

生成器就是一個自定義的迭代器

 

如何得到生成器

  單反函式內出現yield關鍵字時,再去呼叫函式時不會立即執行函式體程式碼,會得到一個返回值,該返回值就是生成器物件,即自定義的迭代器

 

#因為用return的話會直接返回值並且結束程式的執行

 

def func():

  print(‘first’)

  yield 1

  print(''second')

  yield 2

  print('third')

  yield 3

 

g = func()

print(g) #g是一個生成器物件

 

res1 = next(g)

print(res1)

res2 = next(g)

print(res2)

resa3 = next(g)

print(res3)

next(g) # 報錯,沒有值可以取了

 

總結 yield

1.提供了一種自定義迭代器的解決方案

2.yield&return

相同點:都可以返回值,返回值沒有型別限制、個數限制

不同點: return只能返回一次值,yield卻可以讓函式暫停在某一個位置,可以返回多次值

 

來用生成器模擬一個range函式

def calc(start,end,step = 1):

  while start < end:

    yield start

    start += step

 

a = calc(1,10,2) # 返回生成器物件

for i in a:

print(i) # for迴圈取出所有值

 

 

 

 

3. 函式的遞迴呼叫和二分法

  遞迴必須滿足的兩個條件:

    1.每進入下一次遞迴呼叫,問題的規模都應該有所減少

    2.遞迴必須有一個明確的結束條件

以下遞迴只是單純的重複,沒有意義

def func():

  print(1)

  print(2)

  print(3)

  func()

func()

 

def bar():

  print('from bar')

  foo()

 

def foo():

  print('from foo')

  bar()

foo()

 

  遞迴有兩個明確的階段

    1.回溯

    2.遞推

 

如下面的例子

age(5) = age(4)+2

age(4) = age(3)+2

age(3) = age(2)+2

age(2) = age(1)+2

age(1) = 18

 

def age(n):

  if n == 1:

    return 18

  return age(n-1) + 2

print(age(5))

 

 

l=[1,[2,[3,[4,[5,[6,[7,[8,[9,[]]]]]]]]]]  

def func(list1):

  for item in list1:

    if type(item) is not list:

      print(item)

    else:

      func(item)

 

func(l)

 

 

二分法

順序排列的數字中查詢數字

nums=[1,2,3,4,5,6,7,8,9]

def search(nums,find_num):

  if len(nums) == 0:

    print('not exist')

    return

  mid = len(nums) // 2

  if nums[mid] > find_num:

    search(nums[:mid],find_num)

  elif nums[mid]<find_num:

    search(nums[mid+1:],find_num)

  else:

    print('get it')

 

search(nums,9)