python遞歸-三元表達式-列表生成式-字典生成式-匿名函數-部分內置函數-04
- 2019 年 10 月 7 日
- 筆記
遞歸
遞歸: # 函數在調用階段直接或間接地又調用了自身
應用場景: # 將列表中的數字依次列印出來(循環的層數是你必須要考慮的點) –> l = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, [11, [12, [13, ]]]]]]]]]]]]]
# 循環的寫法, 列表嵌套越多層越麻煩 for i in l: # 推導思路 if type(i) is int: print(i) else: for item in i: if type(item) is int: print(item) else: for j in item: if type(item) is int: print(item) else: ... # 函數體的頂用方式(還有一是前面提到過的pass,推薦還是使用 pass來頂替,比較明目) # 下方嵌套多級循環,往裡面取到更下一層列表中的元素 # 遞歸的寫法,程式碼體簡短,不需要考慮循環次數 def get_num(l): for i in l: if type(i) is int: print(i, end=' - ') else: get_num(i) get_num(l) # 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 -
遞歸的優點: 遞歸函數不要考慮循環的次數 只需要把握結束的條件即可
遞歸的兩個階段
回溯:# 從外向里一層一層遞歸調用下去,回溯階段必須要有一個明確的結束條件,每進入下一次遞歸時,問題的規模都應該有所減少(單純地重複調用自身是毫無意義的)
遞推: # 遞推就是從裡向外一層一層結束遞歸
遞歸案例:
def index(): print('from index') login() def login(): print('from login') index() login() # ..........................此處省略一大堆報錯............................................ # File "E:/PyCharm 2019.1.3/ProjectFile/day010/day012/01 函數遞歸.py", line 120, in index # print('from index') # RecursionError: maximum recursion depth exceeded while calling a Python object # 意思是超出了最大遞歸限數
從上述案例中可得知 python解釋限制了遞歸的深度(不然就是無限循環下去,直到你的記憶體溢出,然後。。。emmm)
那麼下面我們就來測試一下 python解釋器中的遞歸深度
# 1.暴力測試 --> 997、998左右 count = 0 def index(): global count print(count) count += 1 index() index() # .....此處省略報錯 # 997 # 最後列印的數字是 997,意味著 python解釋器的遞歸深度約為997 # 2.getrecursionlimit import sys print(sys.getrecursionlimit()) # 不是很精確 # 1000
那麼如何修改默認的遞歸深度呢?
# 修改遞歸深度限制 import sys sys.setrecursionlimit(1100)
應用場景2(有序列表中元素的二分法查找)
# 拋去 成員運算 in 可以直接返回元素在不在列表中,下面探究遞歸運用 # 歧義命名 l_find, # 這裡的 l_find 代表「列表查找」,可能會與下面的往右分割列表查找,往左分割存在歧義 # 所以命名規範里的見名知意也要注意,避免引起歧義 l = [1, 2, 3, 4, 5, 6, 7, 8, 9] # 默認約定輸入的必須是數字(亂輸的咱就不考慮在內了) find_num = int(input("請輸入您要查找的數字>>>:").strip()) def l_find(find_num, l): # print(l) # 可以列印中間步驟 mid_index = len(l) // 2 if not l: # 如果是空列表了,說明該元素不在列表裡 return False # 如果目標元素大於中間元素,那就說明元素在右邊 if find_num > l[mid_index]: # 利用列表的切片知識,將列表切割成一個新的列表,用於遞歸繼續查找 tmp_l = l[mid_index + 1:] res = l_find(find_num, tmp_l) elif find_num == l[mid_index]: # print("get it", find_num) return True # 如果目標元素小於中間元素,那就說明元素在左邊 else: tmp_l = l[0:mid_index] res = l_find(find_num, tmp_l) return res is_exits = l_find(find_num, l) if is_exits: print(f"您所要查找的數字{find_num} 在列表l 中。") else: print(f"您所要查找的數字{find_num} 不在列表l 中。") # 請輸入您要查找的數字>>>:9 # 您所要查找的數字9 在列表l 中。
演算法: 解決問題的高效率的方法(不僅僅局限於數學運算)
三元表達式
先來看這樣一段程式碼
# 比較兩個數的大小 def my_max(x,y): if x > y: return x else: return y
三元表達式實現: res = x if x > y else y ,短短一行就實現了上面函數的功能
三元表達式固定格式: 值1 if 條件 else 值2 ,如果條件成立,返回 值1 ,不成立返回 值2
常見應用場景: 在編程的時候請盡量避免使用三元表達式嵌套,想要知道結果要去推算,不夠直接
is_free = input("請輸入是否免費(y/n)>>>:") is_free = '免費' if is_free == 'y' else '收費' print(is_free) # 請輸入是否免費(y/n)>>>:n # 收費
列表生成式(知識點理解可能有誤)
需求: 給列表中的除了 macbook的名字都加個馬甲 new_ –> new_tank
# 給列表中的除了 macbook的名字都加個馬甲 new_ --> new_tank (macbook被刪掉了這點忽略) # for循環實現 staff = ['tank', 'nick', 'oscar', 'sean', 'macbook'] staff2 = [] for people in staff: if people != 'macbook': staff2.append("new_%s" %people) print(staff2) # ['new_tank', 'new_nick', 'new_oscar', 'new_sean'] # 列表表達式實現 staff = ['tank', 'nick', 'oscar', 'sean', 'macbook'] print(['new_%s' %name for name in staff if name != "macbook"]) # macbook 不滿足條件,所以被濾過了 # ['new_tank', 'new_nick', 'new_oscar', 'new_sean'] # print(['new_' + name for name in staff if name != "macbook"]) # 跟上條語句一樣的執行效果,不過python對推薦字元串直接相加的拼接方式,它的效率十分的低!應盡量避免使用! # 三元表達式結合列表表達式實現 staff = ['tank', 'nick', 'oscar', 'sean', 'macbook'] print([f'new_{name}' if name != 'macbook' else name for name in staff]) # ['new_tank', 'new_nick', 'new_oscar', 'new_sean', 'macbook']
可見列表生成式只用了一行就實現了。
列表生成式原理(if後面可以不寫)
''' 先for循環依次取出列表裡面的元素 然後交由 if 判斷, 條件成立才會把元素交給for 前面的程式碼 如果當前條件不成立, 當前元素直接捨棄 不支援再加else 的情況(for 有 else , if 也有 else 會造成衝突) '''
字典生成式
需求:將 l1 = ['name', 'age', 'hobby'] , l2 = ['jason', 18, 'DBJ'] 兩個列表分別作為鍵值組成一個字典
l1 = ['name', 'age', 'hobby'] l2 = ['jason', 18, 'DBJ'] # for 循環利用字典特性生成字典 d = {} for i in range(len(l1)): d[l1[i]] = l2[i] print(d) # {'name': 'jason', 'age': 18, 'hobby': 'DBJ'} # 利用內置函數 zip,將 l1與l2 組合成元組,然後利用內置函數dict強轉成字典 d1 = dict(zip(l1, l2)) print(d1, zip(l1, l2)) # {'name': 'jason', 'age': 18, 'hobby': 'DBJ'} <zip object at 0x00000248171F21C8> # d2 將l1 與 l2 中的各元素分別作為鍵值組成一個新的字典,過濾掉 age 這一個鍵值 ---> 字典生成式 d2 = {k: v for k, v in zip(l1, l2) if k != 'age'} print(d1, d2) # {'name': 'jason', 'age': 18, 'hobby': 'DBJ'} {'name': 'jason', 'hobby': 'DBJ'}
集合生成式也可以同理推導出來(沒有元組生成器) — > 列表生成器、字典生成器可能還有描述有誤具體還要學到後面才知道。
生成器表達式的意義: 用來創建其他任何類型的序列,增加程式碼可讀性一定程度上可以更高效
列表生成式與三元表達式結合小案例
hello_list = ['halo', 'hi', 'nice to meet you'] hello_list2 = [f"↑{item}" for item in hello_list if len(item) < 10] print(hello_list2) # ['↑halo', '↑hi'] hello_list3 = [f"↑{item}" if len(item) < 10 else item for item in hello_list] # 利用三元表達式實現不同處理 print(hello_list3) # ['↑halo', '↑hi', 'nice to meet you']
匿名函數
匿名函數: 沒有名字的函數
特點: 臨時存在,調用完立即銷毀
關鍵字: lambda
案例:
print(lambda x, y: x + y) # <function <lambda> at 0x000001DAC45B2E18> print((lambda x, y: x + y)(1, 3)) # 4 # :左邊的相當於函數的形參 # :右邊的相當於函數的返回值 # 匿名函數通常不會單獨使用,正常情況下是配合內置函數(也可以是自己寫的函數)一起使用的
內置函數(部分)

max 求最大值、min 求最小值
# 字典值比較 d = { 'egon': 30000, 'jason': 88888888888, 'nick': 3000, 'tank': 1000 } print(max(d, key=lambda name: d[name])) # 比較薪資 返回人名 print(min(d, key=lambda name: d[name])) # key(函數的第二個關鍵字參數)那裡返回什麼,他就比較什麼,最後返回的還是for 循環到的
map 並行遍歷(可接收一個自定義函數)
# map 映射 l = [1, 2, 3, 4, 5, 6] print(list(map(lambda x: x + 5, l))) # 基於for循環 # [6, 7, 8, 9, 10, 11]
zip 並行遍歷
# zip 拉鏈 # 基於for循環 l1 = [1, 2, ] l2 = ['jason', 'egon', 'tank'] l3 = ['a', 'b', 'c'] print(list(zip(l1, l2, l3))) # [(1, 'jason', 'a'), (2, 'egon', 'b')]
filter 過濾
# filter 過濾 l = [1, 2, 3, 4, 5, 6] print(list(filter(lambda x: x != 3, l))) # 基於for循環 # [1, 2, 4, 5, 6]
sorted 排序
# sorted排序 l = ['jason', 'egon', 'nick', 'tank'] print(sorted(l, reverse=True)) # ['tank', 'nick', 'jason', 'egon']
reduce 合併(可指定初值)
from functools import reduce l = [1, 2, 3, 4, 5, 6] print(reduce(lambda x, y: x + y, l, 19)) # 19初始值 第一個參數 # 40 # 當初始值不存在的情況下 按照下面的規律 # 第一次先獲取兩個元素 相加 # 之後每次獲取一個與上一次相加的結果再相加