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]