1. 迭代的概念python
#迭代器即迭代的工具(自定義的函數),那什麼是迭代呢? #迭代:指一個重複的過程,每次重複均可以稱之爲一次迭代,而且每一次重複的結果是下一個迭代的初始值(例如:罰寫做業100遍)
while True: #只是單純地重複,於是不是迭代 print('===>') l=[1,2,3] count=0 while count < len(l): #迭代 print(l[count]) count+=1
2.爲什麼要有迭代器? 什麼是可迭代對象? 什麼是迭代器對象?算法
#一、爲什麼要有迭代器? 對於序列類型:字符串、列表、元組,咱們可使用索引的方式迭代取出其包含的元素。但對於字典、集合、文件等類型是沒有索引的,若還想取出其內部包含的元素,則必須找出一種不依賴於索引的迭代方式,這就是迭代器 #二、什麼是可迭代對象? 可迭代對象指的是內置有__iter__方法的對象,即obj.__iter__,以下 'hello'.__iter__ (1,2,3).__iter__ [1,2,3].__iter__ {'a':1}.__iter__ {'a','b'}.__iter__ open('a.txt').__iter__ #三、什麼是迭代器對象? 可迭代對象執行obj.__iter__()獲得的結果就是迭代器對象 而迭代器對象指的是即內置有__iter__又內置有__next__方法的對象 文件類型是迭代器對象 open('a.txt').__iter__() open('a.txt').__next__() #四、注意: 迭代器對象必定是可迭代對象,而可迭代對象不必定是迭代器對象
3.迭代器對象的使用express
dic={'a':1,'b':2,'c':3} iter_dic=dic.__iter__() #獲得迭代器對象,迭代器對象即有__iter__又有__next__,可是:迭代器.__iter__()獲得的仍然是迭代器自己 iter_dic.__iter__() is iter_dic #True print(iter_dic.__next__()) #等同於next(iter_dic) print(iter_dic.__next__()) #等同於next(iter_dic) print(iter_dic.__next__()) #等同於next(iter_dic) # print(iter_dic.__next__()) #拋出異常StopIteration,或者說結束標誌 #有了迭代器,咱們就能夠不依賴索引迭代取值了 iter_dic=dic.__iter__() while 1: try: k=next(iter_dic) print(dic[k]) except StopIteration: break #這麼寫太醜陋了,須要咱們本身捕捉異常,控制next,python這麼牛逼,能不能幫我解決呢?能,請看for循環
4. for循環原理編程
#基於for循環,咱們能夠徹底再也不依賴索引去取值了 dic={'a':1,'b':2,'c':3} for k in dic: print(dic[k]) #for循環的工做原理 #1:執行in後對象的dic.__iter__()方法,獲得一個迭代器對象iter_dic #2: 執行next(iter_dic),將獲得的值賦值給k,而後執行循環體代碼 #3: 重複過程2,直到捕捉到異常StopIteration,結束循環
5. 迭代器的優缺點數據結構
#優勢: - 提供一種統一的、不依賴於索引的迭代方式 - 惰性計算,節省內存 #缺點: - 沒法獲取長度(只有在next完畢才知道到底有幾個值) - 一次性的,只能日後走,不能往前退
1. 什麼是生成器app
生成器:常規函數定義,可是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每一個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行ide
生成器表達式:相似於列表推導,可是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表函數
#只要函數內部包含有yield關鍵字,那麼函數名()的到的結果就是生成器,而且不會執行函數內部代碼 def func(): print('====>first') yield 1 print('====>second') yield 2 print('====>third') yield 3 print('====>end') g=func() print(g) #<generator object func at 0x0000000002184360>
2.生成器就是迭代器工具
g.__iter__ g.__next__ #因此生成器就是迭代器,所以能夠這麼取值 res=next(g) print(res)
3.生成器Generator總結:性能
本質:迭代器(因此自帶了__iter__方法和__next__方法,不須要咱們去實現)
特色:惰性運算,開發者自定義
4.生成器函數
一個包含yield關鍵字的函數就是一個生成器函數。yield能夠爲咱們從函數中返回值,可是yield又不一樣於return,return的執行意味着程序的結束,調用生成器函數不會獲得返回的具體的值,而是獲得一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推進函數的執行,獲取新的返回值。直到函數執行結束。
import time def generator_fun1(): a = 1 print('如今定義了a變量') yield a b = 2 print('如今又定義了b變量') yield b g1 = generator_fun1() print('g1 : ',g1) #打印g1能夠發現g1就是一個生成器 print('-'*20) #我是華麗的分割線 print(next(g1)) time.sleep(1) #sleep一秒看清執行過程 print(next(g1))
5.生成器有什麼好處呢?
1.延遲計算,一次返回一個結果。也就是說,它不會一次生成全部的結果,這對於大數據量處理,將會很是有用。
# 假如我想讓工廠給學生作校服,生產2000000件衣服,我和工廠一說,工廠應該是先答應下來,而後再去生產,我能夠一件一件的要,也能夠根據學生一批一批的找工廠拿。 # 而不能是一說要生產2000000件衣服,工廠就先去作生產2000000件衣服,等回來作好了,學生都畢業了。。。 def produce(): """生產衣服""" for i in range(2000000): yield "生產了第%s件衣服"%i product_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,好比5件 print(i) num +=1 if num == 5: break #到這裏咱們找工廠拿了8件衣服,我一共讓生產函數(也就是produce生成器函數)生產2000000件衣服。 #剩下的還有不少衣服,咱們能夠一直拿,也能夠放着等想拿的時候再拿
6.練習
#一、自定義函數模擬range(1,7,2) #二、模擬管道,實現時時獲取文件中最新內容
# 一、自定義函數模擬range(1,7,2) def myRange(start,stop,step=1): while start < stop: yield start start += step obj = myRange(1,7,2) print(next(obj)) print(next(obj)) print(next(obj)) print(next(obj)) #StopIteration #二、模擬管道,實現時時獲取文件中最新內容 import time def tail(filename): with open(filename,'r',encoding='utf-8')as f: f.seek(0,2) #從文件末尾開始讀取 while True: line = f.readline() if not line: time.sleep(0.5) continue yield line obj = tail('a.txt') for i in obj: print(i)
7.協程函數
什麼是協程:
協程是一個無優先級的子程序調度組件,容許子程序在特色的地方掛起恢復。(相似於看電影時的暫停播放)
線程包含於進程,協程包含於線程。只要內存足夠,一個線程中能夠有任意多個協程,但某一時刻只能有一個協程在運行,多個協程分享該線程分配到的計算機資源。
#yield關鍵字的另一種使用形式:表達式形式的yield def eater(name): print('%s 準備開始吃飯啦' % name) food_list=[] while True: food = yield food_list print('%s 吃了 %s' % (name, food)) food_list.append(food) e=eater('鋼蛋') print(e.send(None)) #初始化,對於表達式形式的yield,在使用時,第一次必須傳None,e.send(None)等同於next(e) print(e.send('包子')) print(e.send('韭菜餡包子')) print(e.send('大蒜包子')) e.close() #關閉 print(e.send('大麻花'))
#send 獲取下一個值的效果和next基本一致 #只是在獲取下一個值的時候,給上一yield的位置傳遞一個數據 #使用send的注意事項 # 第一次使用生成器的時候 是用send(None)或者 next進行初始化
8.練習:
一、編寫裝飾器,實現初始化協程函數的功能
def init(func): def inner(*args,**kwargs): res = func(*args,**kwargs) next(res) #在裝飾器中執行初始化方法 return res return inner @init def eater(name): print('%s 準備開始吃飯啦' % name) food_list=[] while True: food = yield food_list print('%s 吃了 %s' % (name, food)) food_list.append(food) e=eater('鋼蛋') # e.send(None) print(e.send('包子')) print(e.send('韭菜餡包子')) print(e.send('大蒜包子'))
9. yield 關鍵字 總結
一、把函數作成迭代器 二、對比return,能夠返回屢次值,能夠掛起/保存函數的運行狀態
1.列表推導式
#一、示例 egg_list=[] for i in range(10): egg_list.append('雞蛋%s' %i) print(egg_list) #列表推導式1 egg_list = ['臭雞蛋%s' %i for i in range(0,10) ] print(egg_list) #列表推導式2 egg_list = ['臭雞蛋%s' %i for i in range(0,10) if i>6] print(egg_list) #二、語法 [expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN ] 相似於 res=[] for item1 in iterable1: if condition1: for item2 in iterable2: if condition2 ... for itemN in iterableN: if conditionN: res.append(expression) #三、優勢:方便,改變了編程習慣,可稱之爲聲明式編程
2.生成器表達式
#生成器表達式 #一、把列表推導式的[]換成()就是生成器表達式 #二、示例:生一筐雞蛋變成給你一隻老母雞,用的時候就下蛋,這也是生成器的特性 chicken=('雞蛋%s' %i for i in range(5)) print(chicken) # generator object <genexpr> at 0x10143f200> print(next(chicken)) #'雞蛋0' print(list(chicken)) #因chicken可迭代,於是能夠轉成列表 ['雞蛋1', '雞蛋2', '雞蛋3', '雞蛋4',] #三、優勢:省內存,一次只產生一個值在內存中
總結:
1.把列表解析的[]換成()獲得的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
4.聲明式編程練習題
一、將names=['egon','alex_sb','wupeiqi','yuanhao']中的名字所有變大寫 二、將names=['egon','alex_sb','wupeiqi','yuanhao']中以sb結尾的名字過濾掉,而後保存剩下的名字長度 3、求文件a.txt中最長的行的長度(長度按字符個數算,須要使用max函數) 4、求文件a.txt中總共包含的字符個數?思考爲什麼在第一次以後的n次sum求和獲得的結果爲0?(須要使用sum函數) 5、思考題 with open('a.txt') as f: g=(len(line) for line in f) print(sum(g)) #爲什麼報錯?
# 一、將names=['egon','alex_sb','wupeiqi','yuanhao']中的名字所有變大寫 names=['egon','alex_sb','wupeiqi','yuanhao'] names = [name.upper() for name in names] print([name.upper() for name in names]) # 二、將names=['egon','alex_sb','wupeiqi','yuanhao']中以sb結尾的名字過濾掉,而後保存剩下的名字長度 names=['egon','alex_sb','wupeiqi','yuanhao'] names = [len(name) for name in names if not name.endswith("sb") ] print(names) # 三、求文件a.txt中最長的行的長度(長度按字符個數算,須要使用max函數) with open('a.txt','r',encoding='utf-8')as f: print(max(len(i) for i in f)) #最後有一個換行符 # 四、求文件a.txt中總共包含的字符個數?思考爲什麼在第一次以後的n次sum求和獲得的結果爲0?(須要使用sum函數) with open('a.txt','r',encoding='utf-8') as f: print(sum(len(i) for i in f)) print(sum(len(i) for i in f)) #緣由讀取文件的指針已經到文件的末尾了 # 五、思考題 with open('a.txt') as f: g=(len(line) for line in f) print(sum(g)) #爲什麼報錯? #答: with open 在執行完文件操做後,會自動關閉掉此文件,因此再使用文件內容時會報錯
1. 遞歸調用的定義
#遞歸調用是函數嵌套調用的一種特殊形式,函數在調用時,直接或間接調用了自身,就是遞歸調用
2.遞歸分爲兩個階段:遞推,回溯
#問年齡遊戲 #圖解。。。 # age(4) = age(3) + 2 # age(3) = age(2) + 2 # age(2) = age(1) + 2 # age(1) = 40 def age(n): if n == 1: return 40 else: return age(n-1)+2 print(age(4))
3.遞歸的使用
#總結遞歸的使用: 1. 必須有一個明確的結束條件 2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小 3. 遞歸效率不高,遞歸層次過多會致使棧溢出(在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀, 每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出) 4.遞歸默認調用的最大深度爲 ---997
4.設置遞歸最大深度
#設置遞歸最大深度 #注意:實際上能夠達到的深度 取決於計算機的性能了 import sys sys.setrecursionlimit(10000) #遞歸最大深度 def func1(n): print(">>>>>>",n) n+=1 func1(n) func1(0)
5.練習題
1.使用遞歸函數完成三級菜單
menu = { '北京': { '海淀': { '五道口': { 'soho': {}, '網易': {}, 'google': {} }, '中關村': { '愛奇藝': {}, '汽車之家': {}, 'youku': {}, }, '上地': { '百度': {}, }, }, '昌平': { '沙河': { '老男孩': {}, '北航': {}, }, '天通苑': {}, '回龍觀': {}, }, '朝陽': {}, '東城': {}, }, '上海': { '閔行': { "人民廣場": { '炸雞店': {} } }, '閘北': { '火車戰': { '攜程': {} } }, '浦東': {}, }, '山東': {}, }
def threeLM(dic): while True: for k in dic:print(k) key = input('input>>').strip() if key == 'b' or key == 'q':return key elif key in dic.keys() and dic[key]: ret = threeLM(dic[key]) if ret == 'q': return 'q' threeLM(menu) #遞歸函數實現三級菜單
想從一個按照從小到大排列的數字列表中找到指定的數字,遍歷的效率過低,用二分法(算法的一種,算法是解決問題的方法)能夠極大低縮小問題規模
二分查找法 簡單版
#二分查找法 l=[1,2,10,30,33,99,101,200,301,402] #從小到大排列的數字列表 count = 0 # def search(num,l): global count count+=1 if l: print(l) #查看列表的變化 mid = (len(l)-1)//2 if num > l[mid]: l=l[mid+1:] # 取列表右邊數據 elif num < l[mid]: l =l[:mid] # 取列表左邊數據 else: print(l[mid]) return #經過return 終止遞歸 search(num,l) #遞歸循環調用 else: print("沒有找到指定數字") search(10,l) print(count)
二分查找法升級版
#二分查找法 升級版 l=[1,2,10,30,33,99,101,200,301,402] def search(num,l,start=0,stop=len(l)-1): if start <= stop: mid=start+(stop-start)//2 print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' %(start,stop,mid,l[mid])) if num > l[mid]: start=mid+1 elif num < l[mid]: stop=mid-1 else: print('find it',mid) return search(num,l,start,stop) else: #若是stop > start則意味着列表實際上已經所有切完,即切爲空 print('not exists') return search(101,l)
#1.使用遞歸打印斐波那契數列(前兩個數的和獲得第三個數,如:0 1 1 2 3 4 7...) #2.一個嵌套不少層的列表,如l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]],用遞歸取出全部的值
#1.使用遞歸打印斐波那契數列(前兩個數的和獲得第三個數,如:0 1 1 2 3 4 7...) #非遞歸 def fib(n): a,b=0,1 while a < n: print(a,end=' ') a,b=b,a+b print() fib(10) #遞歸 def fib(a,b,stop): if a > stop: return print(a,end=' ') fib(b,a+b,stop) fib(0,1,10) #2. 一個嵌套不少層的列表,如l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]],用遞歸取出全部的值 l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]] def get(seq): for item in seq: if type(item) is list: get(item) else: print(item) get(l)