python迭代器-迭代器取值-for循環-生成器-yield-生成器表達式-常用內置方法-面向過程編程-05

  • 2019 年 10 月 7 日
  • 筆記

迭代器

迭代器

迭代: # 更新換代(其實也是重複)的過程,每一次的迭代都必須基於上一次的結果(上一次與這一次之間必須是有關係的)

迭代器: # 迭代取值的工具

為什麼用迭代器: # 迭代器提供了一種可以不依賴索引取值的方式

# 會一直列印0,記得停止  # n = 0  # while True:  #     print(n)    # 重複 + 每次迭代都是基於上一次的結果而來的  l = [1, 2, 3, 4, 5]  s = 'hello'  n = 0  while n < len(s):      print(s[n])      n += 1  # h  # e  # l  # l  # o

可迭代對象

'''  可迭代對象      只要內置有 __iter__ 方法的都叫做可迭代對象(讀:雙下iter)          補充: 針對雙下劃線開頭結尾的方法,推薦讀「雙下 + 方法名」      基本數據類型中,是可迭代對象的有(str, list, tuple, dict, set), 文件對象(執行 __iter__ 之後還是本身,__next__之後還是他本身)        可迭代對象執行內置的 __iter__ 方法,得到的就是該對象的迭代器對象  '''
n = 1  f = 1.1  s = 'hello'  l = [1,2,34,]  t = (1,2,34)  s1 = {1,2,3,4}  d = {'name':'jason'}  f1 = open('xxx.txt','w',encoding='utf-8')    #  # res = s.__iter__()  # res = iter(s)  #  # print(s.__len__())  # 簡化成了len(s)  # res1 = l.__iter__()  # res1 = iter(l)  # res2 = f1.__iter__()  # res2 = iter(f1)  # print(res,res1,res2)  # print(f1)    # 可迭代對象執行內置的__iter__方法得到就是該對象的迭代器對象

迭代器對象與可迭代對象的區別

'''  迭代器對象和可迭代對象有什麼共同點      迭代器對象          1.內置有雙下iter( __iter__) 方法          2.內置有 __next__ 方法          ps:迭代器對象一定是可迭代對象,而可迭代對象不一定是迭代器對象  '''

__iter__轉換迭代器對象

l = [1, 2, 3, 4]  # 生成一個迭代器對象  iter_l = l.__iter__()    # 迭代器取值 調用__next__  print(iter_l.__next__())  print(iter_l.__next__())  print(iter_l.__next__())  print(iter_l.__next__())  # 1  # 2  # 3  # 4  # print(iter_l.__next__())  # 取完了 直接報錯      d = {'name':'jason','password':'123','hobby':'泡m'}  # 將可迭代對象d轉換成迭代器對象  iter_d = d.__iter__()  # 迭代器對象的取值 必須用__next__  print(iter_d.__next__())  print(iter_d.__next__())  print(iter_d.__next__())  # name  # password  # hobby  # print(iter_d.__next__())  # 取完了 報錯StopIteration    f1 = open('test1.txt', 'r', encoding='utf-8')  # 調用f1內置的__iter__方法  iter_f = f1.__iter__()  print(iter_f is f1)  # True  """  迭代器對象無論執行多少次__iter__方法得到的還是迭代器對象本身(******)  """  print(f1 is f1.__iter__().__iter__().__iter__().__iter__())  # True

# 迭代器對象無論執行多少次__iter__方法得到的還是迭代器對象本身(******)

疑問: # __iter__方法就是用來幫我們生成迭代器對象,而文件對象本身就是迭代器對象,為什麼還內置有__iter__方法??? ---見for循環原理

異常處理

d = {'name': 'jason', 'password': '123', 'hobby': '泡m'}  iter_d = d.__iter__()  # 異常處理  while True:      try:          print(iter_d.__next__())      except StopIteration:          print('老母豬生不動了')          break  # name  # password  # hobby  # 老母豬生不動了

迭代器與迭代器對象總結

'''  可迭代對象:內置有 __iter__ 方法  迭代器對象:既內置有__iter__ 方法,也內置有__next__方法    迭代取值的優點與缺點:      優點:          不依賴於索引取值          記憶體中永遠只佔一份空間,不會導致記憶體溢出      缺點:          不能獲取指定的元素          取完之後會報StopIteration錯..  '''

for循環內部原理

'''  for 循環內部的本質      1.將in後面的對象調用 __iter__ 轉換成迭代器對象   --> 所以前面的疑問,文件對象本來就是迭代器對象還有 __iter__ 方法      2.調用__next__ 迭代取值      3.內部有異常捕獲,StopIteration,當__next__報這個錯,自動結束循環  '''

 內部原理同上面異常處理里的案例

生成器

生成器: # 用戶自定義的迭代器,本質就是迭代器

注意點: # 函數內如果有yield關鍵字,那麼加括弧執行函數的時候並不會觸發函數體程式碼的運行 , # yield後面跟的值就是調用迭代器__next__方法你能得到的值 , # yield既可以返回一個值也可以返回多個值 並且多個值也是按照元組的形式返回

 yield 返回值(體現上面的注意點)

def func():      print('first')      yield  # 函數內如果有yield 關鍵字,那麼加括弧執行函數的時候並不會觸發函數體程式碼的運行  ***      print('second')      yield 2  # yield 後面跟的值就是調用迭代器 __next__ 方法你能得到的值      print('third')      yield 3, 4, 5  # 既可以返回一個值,也可以返回多個值,並且多個值也是按照元組的形式返回      g = func()  # 生成器初始化:將函數變成迭代器  # g.__iter__()  print(g.__next__())  # first  # None    print(g.__next__())  # second  # 2    print(g.__next__())  # third  # (3, 4, 5)

小案例

# 自定義range 功能  for i in range(10):      print(i, end='- ')  print()      def my_range(start, end, step=1):  # 只傳一個參數的,range,判斷參數個數  搜:python只傳一個參數的range      while start < end:          yield start          start += step      for i in my_range(1, 10, 3):      print(i)  # 1  # 4  # 7

yield表達式形式傳入參數

# yield 表達式形式(傳參的第三種方式,直接傳參,閉包)  def dog(name):      print("%s 準備開始吃" %name)      while True:          food = yield          print("%s 吃了 %s" %(name, food))      print(dog)  # <function dog at 0x000001C22A10BA60>    # 當函數內有yield 關鍵字的時候 調用該函數 不會執行函數體程式碼  #   而是將函數變成生成器  g = dog("egon")  # 把函數變成生成器,函數體沒有運行  g.__next__()  # 不運行這個直接運行下面的會直接報錯 TypeError: can't send non-None value to a just-started generator  # 必須將程式碼運行至 yield 才能為其傳值  # egon 準備開始吃  g.send('狗不理包子')  # 給yield 左邊的變數傳參,觸發了 __next__ 方法  # egon 吃了 狗不理包子  g.__next__()  # egon 吃了 None  g.send('餃子')  # egon 吃了 餃子

總結yield並與return作對比

'''  總結yield:      1.yield 提供了一種自定義生成器的方式      2.yield 會幫你將函數的運行狀態暫停住      3.yield 可以返回值    與return 之間的異同點:      相同點:          1.都可以返回值,並且都可以返回多個,返回多個都是包裝成元組      不同點:          1.yield 可以返回多次值,而return  只能返回一次函數立即結束          2.yield還可以接收外部傳入的值    '''

常用內置方法

常見數學內置函數 abs 絕對值      pow 求平方    round  四捨五入   divmod 將運算結果除數與餘數存入元組

# abs 求絕對值  print(abs(-10.11))  # 10.11    # pow 平方  print(pow(2, 4))  # 16    # round 四捨五入  print(round(3.1656))  # 3    # divmod 將運算結果除數與餘數存入元組(可用於分頁器)  print(divmod(100, 11))  # (9, 1)  all_count = 101  counts_each_page = 5  total_pages, more = divmod(all_count, counts_each_page)  if more:      total_pages += more  print(total_pages)  # 21  --> 共101條內容,按每頁5條來分,需要分21頁

數據類型進位轉換  isinstance(判斷數據類型)  all(傳過來容器類型必須全部都是True才返回True)   any(有一個是True就返回True)   bool(將傳進來參數變成布爾類型)   bin(十轉二) oct(十轉八)  hex(十轉十六)    int(把指定類型轉成十進位)

# isinstance 判斷數據類型,後面統一用該方法判斷對象是否屬於某一個數據類型  n = 1  print(type(n))  print(isinstance(n, list))  # 判斷對象是否屬於某個數據類型  # <class 'int'>  # False    # all 傳過來的容器類型里必須全部是True,否則返回False  # any 只要一個True,就返回True  l = [0, 1, 2]  print(all(l))  # 只要有一個是False,就返回False  print(any(l))  # False  # True    # bool  print(bool(1))  print(bool(0))  # True  # False      # bin  oct  hex 將十進位轉換成對應的進位  print(bin(10))  print(oct(10))  print(hex(10))  # 0b1010 二進位  # 0o12 八進位  # 0xa 十六進位    # int 可以把指定進位轉成十進位  print(int('0b1010', 2))  # 10

字元編碼  ascii (個人覺得沒啥用)  chr(將數字轉換成ascii碼 對應的字元)    ord (將字元按照ascii碼 轉成數字)  bytes(轉成二進位)

# ascii  個人覺得沒啥用  print(ascii('a'))  # 'a'  print(ascii(65))  # 65  print(ascii('c'))  # 'c'    # chr  ord 將數字轉換成ascii 碼錶對應的字元  將字元按照ascii 碼錶轉成對應的數字  print(chr(65))  # A  print(ord('A'))  # 65    # bytes 轉換成二進位  # s = b'hello'  # print(s)  # # b'hello'  s = 'hello'  print(s.encode('utf-8'))  print(bytes(s, encoding='utf-8'))  # b'hello'  # b'hello'

可迭代對象常見內置函數  slice  切片(生成的是老母豬,節省空間)  enumerate  給可迭代對象生成索引(假索引)

# slice 切片  myslice = slice(5)    # 設置截取5個元素的切片  arr = range(10)  print(arr)  print(arr[myslice])  # 截取5個元素  print(arr)  # range(0, 10)  # range(0, 5)  # range(0, 10)      # enumerate 枚舉 可以指定起始位  l = ['a', 'b', 'c']  for i, j in enumerate(l, 1):      print(i, j)  # 1 a  # 2 b  # 3 c

執行字元串中的python語句以及python字元串格式化  eval (執行字元串中的python語句,不夠強大)   exec (執行python中的語句,能執行邏輯的)   format(格式化字元串)

# eval  exec 執行字元串里的程式碼,eval 不支援邏輯程式碼,只支援一些簡單的python程式碼, exec可以執行邏輯程式碼  s = """  print('hello baby~')  # x = 1  # y = 2  # print(x + y)  """    # eval(s)  # hello baby~  exec(s)  # hello baby~    # format 三種玩法  # ------ {} 佔位 --------  print('{} like {}'.format('tank', 'jason'))  # tank like jason  # ------ {index} 索引 --------  print('{0} like {2}'.format('tank', 'egon', 'jason'))  # tank like jason  # ------ {name} 指名道姓(關鍵字) --------  print('{name} is {age} years old.'.format(age=18, name='jason'))  # jason is 18 years old.

對象命名空間 callable(對象是否可調用)  dir (獲取該對象名稱空間里的名字)  globals (查看全局名稱空間的名字)    locals (返回當前位置名稱空間的名字)  help (查看函數的注釋)

# callable 可調用的,可以加括弧執行相應功能  def index():      pass  l = [1, 2, 3, 4, 5]  print(callable(index))  print(callable(l))  # True  # False    # dir 獲取當前對象名稱空間裡面的名字(所有他能夠支援的名字),可以是任何對象  l = [1, 2, 3]  print(dir(l))  # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']  # import test  # print(dir(test))  # print(test.name)      # globals 無論在哪,查看的都是全局名稱空間  # locals 當前語句在那個位置,就會返回哪個位置所存儲的所有名字  def index():      '''      index的注釋      :return: 沒有返回值      '''      username = "局部名稱空間里的username"      print(locals())  # 當前語句在那個位置,就會返回哪個位置所存儲的所有名字      print(globals())  # 不受位置影響  # {'__name__': '__main__'-------省略一大堆------- 'l': ['a', 'b'], 'i': 2, 'j': 'b'.....>}  index()  # {'username': '局部名稱空間里的username'}      # help 查看函數注釋  print(help(index))  # Help on function index in module __main__:  #  # index()  #     index的注釋  #     :return: 沒有返回值  #  # None

面向過程編程

概述: # 面向過程就類似於流水線設置,將複雜問題分步實現

優點: # 可以將複雜的問題流程化,從而簡單化

缺點: # 可擴展性差,如有改動,可能會涉及超大面積更改

小案例(牽一髮動全身)

# 獲取用戶登錄  def get_info():      while True:          username = input(">>>:").strip()          if not username.isalpha():  # 判斷字元串不能包含數字              print('不能包含數字')              continue          password = input('>>>:').strip()          confirm_password = input("confirm>>>:").strip()          if password == confirm_password:              operate_data(username,password)              break          else:              print('兩次密碼不一致')    # 2.處理用戶資訊  def operate_data(username,password):      # jason|123      res = '%s|%sn'%(username,password)      save_data(res,'userinfo.txt')    # 3.存儲到文件中  def save_data(res,file_name):      with open(file_name,'a',encoding='utf-8') as f:          f.write(res)    def register():      get_info()    register()
# 在用戶註冊時候讓用戶選擇用戶類型  # 1.獲取用戶輸入  def get_info():      while True:          username = input(">>>:").strip()          if not username.isalpha():  # 判斷字元串不能包含數字              print('不能包含數字')              continue          password = input('>>>:').strip()          confirm_password = input("confirm>>>:").strip()          if password == confirm_password:              # 改動 ---------------------------------------------              d = {                  '1':'user',                  '2':'admin'              }              while True:                  print("""                      1 普通用戶                      2 管理員                  """)                  choice = input('please choice user type to register>>>:').strip()                  if choice not in d:continue                  user_type = d.get(choice)                  # 函數的參數個數也要改變                  operate_data(username,password,user_type)                  break              # 改動 ----------------------------------------------          else:              print('兩次密碼不一致')    # 2.處理用戶資訊  # 函數定義時的的參數個數也要改變 ------------------------------------------  def operate_data(username,password,user_type):      # jason|123      # 拼接也要改動 ------------------------------------------      res = '%s|%s|%sn'%(username,password,user_type)      save_data(res,'userinfo.txt')    # 3.存儲到文件中  def save_data(res,file_name):      with open(file_name,'a',encoding='utf-8') as f:          f.write(res)    def register():      get_info()    register()

 擴展練習題(面試題)

1.請判斷下面程式碼中的 res 是什麼

def add(n, i):      return n + i  def test():      for i in range(4):          yield i  g = test()  for n in [1, 10]:      g = (add(n, i) for i in g)  res = list(g)# A. res = [10, 11, 12, 13]  # B. res = [11, 12, 13, 14]  # C. res = [20, 21, 22, 23]  # D. res = [21, 22, 23, 24]
# C. res = [20, 21, 22, 23]

2.請判斷下面程式會輸出什麼

# 已經提前把裡面的東西取出來了

3.利用生成器表達式讀取文件行數

'''  需求: 讀取一個文件並返回每行數據的長度  '''  with open('test1.txt', 'w', encoding='utf-8') as f:      for line in range(1000):          f.write(f'www{line}aaa' * (line + 1) + 'n')
# 列表推導式: 處理數據量大的文件會導致記憶體溢出.  res = [len(line) for line in open('test1.txt', 'r', encoding='utf-8')]  # print(res)      # 生成器表達式: 處理數據量大的文件推薦使用.  res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8'))  print(res2)  # <generator object <genexpr> at 0x000002B3748FD0A0>  print(next(res2))  print(next(res2))

4.寫出下面程式碼的執行結果

# 請寫出下面程式碼的執行結果  def mutipliers():      return [lambda x: i*x for i in range(4)]      print([m(2) for m in mutipliers()])
# [6,6,6,6]  ---> 題目的正確答案  def multipliers():        return [lambda x, i=i: i*x for i in range(4)]      # 0, 1, 2, 3      # [func(x): return 0*x, func(x): return 1*x,      # func(x): return 2*x, func(x): return 3*x, ]    print([m(2) for m in multipliers()])  # [0, 2, 4, 6]    # [func(x): return 0*2, func(x): return 1*2,  # func(x): return 2*2, func(x): return 3*2, ]  # [0, 2, 4, 6]  # [6, 6, 6, 6]    # 閉包函數的延遲綁定  # 在內層函數執行時才會綁定變數i  def multipliers2():      list1 = []      for i in range(4):            def func(x, i=i):                return x * i            list1.append(func)        return list1    print([m(2) for m in multipliers2()])  # [0, 2, 4, 6]    # [0, 2, 4, 6]

Tags: