1. 程式人生 > 其它 >遞迴,二分法,生成式和迭代器,及for 迴圈本質

遞迴,二分法,生成式和迭代器,及for 迴圈本質

遞迴函式

"""
遞迴:函式在執行過程中 直接或者間接的呼叫了自身
"""

# 官網表示:python預設的最大遞迴深度為1000次(根據CPU不同一般在997-1000次。)
import sys
print(sys.getrecursionlimit())  # 檢視預設遞迴深度
# print(sys.setrecursionlimit(2000))  #手工設定遞迴呼叫深度,2000可以換成任意數。


count = 1
def index():
    global count
    count += 1
    print(count)
    print('from index')
    index()
index()

# def func():
#     print('from func')
#     index()
# def index():
#     print('from index')
#     func()
# index()


"""
遞迴
    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 get_age(n):
#     if n == 1:
#         return 18
#     return get_age(n - 1) + 2
# print(get_age(5))


l = [1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,]]]]]]]]]]]]]]
# 打印出列表中每一個元素(列表除外)
# 1.迴圈該列表 獲取列表內每一個元素
# 2.判斷該元素是否是數字 如果是數字 則直接列印
# 3.如果是列表 則迴圈該列表 獲取列表內每一個元素
# 4.判斷該元素是否是數字 如果是數字 則直接列印
# 5.如果是列表 則迴圈該列表 獲取列表內每一個元素
# 6.判斷該元素是否是數字 如果是數字 則直接列印
# 7.如果是列表 則迴圈該列表 獲取列表內每一個元素
def get_num(l):
    for i in l:
        if type(i) is int:
            print(i)
        else:
            # 也是for迴圈 然後判斷
            get_num(i)
get_num(l)

# for i in []:
#     print(i,'懵逼了!')

演算法之二分法

# 什麼是演算法?
	解決問題的高效方法
   
# 二分法(入門級別:還有一定距離)
	l = [11, 23, 43, 57, 68, 76, 81, 99, 123, 321, 432, 567, 666, 712, 899, 999, 1111]
	
	
# 第一種方式  直接for迴圈從左往右依次查詢

# 第二種方式  二分法


"""
二分法能夠使用的場景  資料集必須有序
"""


def my_partner(target_num, list1):  # 設定一個函式my_partner.裡面2個形參,target_num和列表list1
    if len(list1) == 0:  # 如果列表長度索引為0,即沒有元素,提示使用者沒有這個數。返回給使用者說沒有這個數。·
        print('沒有這個數')
        return
    middle_index = len(list1) // 2  # 分兩半  結果是每一半為8
    if target_num > list1[middle_index]:  # 判斷中間索引對應的值比目標值大還是比目標值小。
        list1_right = list1[middle_index + 1:]  # 獲取列表list1 索引9以後的所有元素
        print(list1_right)  # 驗證一下是否獲取到了列表list1右邊的那一段元素
        my_partner(target_num, list1_right)  # 返回函式初始再次執行,即:重新判斷重新切片列表
    elif target_num < list1[middle_index]:  # 如果要找的數字比列表中間索引對應的值小,那麼往左邊找
        list1_left = list1[:middle_index]  # 把列表左邊切片下來
        print(list1_left)  # 列印驗證一下列表
        my_partner(target_num, list1_left)  # 返回函式初始再次執行,即:重新判斷重新切片列表
    else:  # 其他情況(即要找的數字不符合上面條件,等於找到的數字)就提示使用者找到了
        print('提示使用者找到了')
# my_partner(444, l)  找不到 需要新增結束條件
# my_partner(11, l)  # 要查詢的元素在開頭  那麼還沒有依次查詢的效率高

三元表示式

# def my_max(a, b):
#     if a > b:
#         return a
#     else:
#         return b



"""
當功能需求僅僅是二選一的情況下 那麼推薦使用三元表示式
"""



# def my_max(a, b):
#     return a if a > b else b



"""
條件成立採用if前面的值 if 條件 else 條件不成立採用else後面的值
三元表示式儘量不要巢狀使用
"""


# res = '乾飯' if 10 > 2 else '不幹飯'
# print(res)
# res = '乾飯' if 10 > 2 else ('不管飯' if 2 >5 else '寫的啥!')
# print(res)


# is_free = input('電影是否收費(y/n)>>>:').strip()
# if is_free == 'y':
#     print('收費')
# else:
#     print('免費')
# print('收費' if is_free == 'y' else '免費')
username = input('username>>>:')
res = 'NB' if username == 'jason' else 'SB'
print(res)

列表生成式

name_list = ['jason', 'kevin', 'tony', 'jerry']
# 給列表中所有的人名加上_DSB字尾
'''傳統做法'''
# 1.定義一個空列表
# new_list = []
# 2.for迴圈老列表
# for name in name_list:
# 3.生成新的名字
# new_name = '%s_DSB'%name
# 4.新增到新的列表中
# new_list.append(new_name)
# print(new_list)
'''列表生成式'''
# res = ['%s_DSB' % name for name in name_list]
# print(res)


'''傳統做法'''
# 1.定義一個空列表
# new_list = []
# # 2.for迴圈老列表
# for name in name_list:
#     # 3.生成新的名字
#     if name == 'jason':
#         continue
#     else:
#         new_name = '%s_DSB'%name
#         # 4.新增到新的列表中
#         new_list.append(new_name)
# print(new_list)
'''列表生成式'''
# res = ['%s_DSB' % name for name in name_list if name != 'jason']
# print(res)

字典生成式

# l1 = ['name', 'age', 'hobby']
# l2 = ['jason', 18, 'read']
# new_dict = {}
# for i in range(len(l1)):
#     new_dict[l1[i]] = l2[i]
# print(new_dict)

# count = 0
# for i in l1:
#     print(count,i)
#     count += 1

# 列舉
'''
enumerate(l1)
    針對該方法使用for迴圈取值 每次會產生兩個結果
        第一個是從0開始的數字
        第二個是被迴圈物件裡面的元素
    還可以通過start引數控制起始位置
'''
# for i, j in enumerate(l1, start=1):
#     print(i, j)


name_list = ['jason', 'kevin', 'tony', 'jerry']
# res = {i: j for i, j in enumerate(name_list) if j != 'jason'}
# print(res)

# res1 = {i for i,j in enumerate(name_list)}
# print(res1,type(res1))


# 迭代器
res2 = (i for i,j in enumerate(name_list))
print(res2)

匿名函式

# 匿名函式:沒有名字的函式
"""
語法格式
    lambda 形參:返回值
"""
# print(lambda x:x**2)
# def index():
#     pass
# print(index)
# print((lambda x: x ** 2)(2))
# res = lambda x: x ** 2
# print(res(2))

'''匿名函式一般不會單獨使用 都是配合其他函式一起使用'''
# map()  對映
# l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# def index(n):
#     return n ** 2
# print(list(map(lambda x:x**2, l)))

常用內建函式

# 1.map()  對映
l = [1,2,3,4]
map(lambda x:x+1,l)  # 迴圈獲取列表中每個元素並傳遞給匿名函式儲存返回值

# 2.zip()  拉鍊
l = [11, 22, 33, 44, 55, 66, 77]
name_list = ['jason', 'kevin', 'tony', 'jerry']
l1 = [1, 2, 3, 4, 5, 6, 7]
l2 = [8, 7, 6, 4, 3, 2, 1]
# new_list = []
# for i in range(len(l)):
#     new_list.append((l[i],name_list[i]))
# print(new_list)
new_list = zip(l, name_list, l1, l2)
print(list(new_list))
# 返回結果:[(11, 'jason', 1, 8), (22, 'kevin', 2, 7), (33, 'tony', 3, 6), (44, 'jerry', 4, 4)]
# 把多個列表的元素拼接在新列表,拼接次數按照最少的索引列表(即name_list  4個元素拼接4次),多餘的元素直接捨棄。

# 3.max與min  max求最大值 min求最小值
# 列表:
# l = [11, 22, 33, 44, 55, 66, 77]
# print(max(l))
# print(min(l))


# 字典
# 字典不能直接max求最大值print(max(d)),而是應該套用函式。
# 現在我們有這樣一個字典:
d = {
    'jason':3000,
    'Bevin':1000000,
    'Ascar':10000000000,
    'aerry':88888
}
def index(key):
    return d[key]
print(max(d,key=lambda key:d[key]))  # for迴圈先取值 之後再比較大小
"""
直接比max比大小是比key的大小。
Key屬於字串型別,按照ASCII碼錶
A-Z  65-90
a-z  97-122
"""
# 最小值
print(min(d,key=lambda key:d[key]))  # jason

# 4.filter  過濾
l = [11, 22, 33, 44, 55]
res = filter(lambda x: x > 30, l)# 過濾掉30以下的元素
print(list(res))  # [33, 44, 55]

# 5.reduce  歸總
from functools import reduce
d = [11, 22, 33, 44, 55, 66, 77, 88, 99]
res = reduce(lambda x, y: x + y, d)# 把全部元素加起來得到的數,返回結果是495
res1 = reduce(lambda x, y: x + y, d, 100)  # 還可以額外新增元素值.返回結果是595
print(res)

可迭代物件

# 迭代
	迭代即更新換代 每次的更新都必須依賴於上一次的結果
    
'''迭代其實給我們提供了一種不依賴索引取值的方式'''

# 可迭代物件
	內建有__iter__方法的都稱之為可迭代物件	
    	內建的意思是可以通過點的方式直接檢視到的
    """
    針對雙下滑線開頭 雙下滑線結尾的方法 最為專業標準的讀法為
    	雙下方法名
    	
    面向物件的時候為了與隱藏變數區分開
    """
# 以下屬於迭代,每次的更新都必須依賴於上一次的結果
# n = 1
# while True:
#     n+=1
#     print(n)

# l = [11,22,33,44,55,66]
# n = 0
# while n < len(l):
#     print(l[n])
#     n += 1


# __iter__ 雙下iter的內建方法
i = 12  # 整型沒有
f = 11.11  # 浮點型沒有
s = 'jason'  # 字串有
l = [111,22,33,4]  # 列表有
d = {'username':'jason','pwd':123}  # 字典有
t = (11,22,33)  # 元祖有
se = {11,22,33}  # 集合有
b = True  # 布林值沒有
# file = open(r'a.txt','w',encoding='utf8')# 檔案物件有

"""
含有__iter__的有
    字串 列表 字典 元組 集合 檔案物件
上述通常為可迭代物件
"""


print(d)
print(d.__iter__())  # 等價於呼叫了一個內建方法 d.get()
print(iter(d))
print(d.__len__())
print(len(d))
"""
可迭代物件呼叫__iter__方法會變成迭代器物件(老母豬)

__iter__方法在呼叫的時候還有一個簡便的寫法iter()
    一般情況下所有的雙下方法都會有一個與之對應的簡化版本 方法名()
	例如__len__可以簡便寫成len()  用來統計長度
"""

迭代器物件

"""
迭代器物件
    即含有__iter__方法 又含有__next__方法
如何生成迭代器物件
    讓可迭代物件執行__iter__方法

檔案物件本身即是可迭代物件又是迭代器物件
迭代器物件無論執行多少次__iter__方法 還是迭代器物件(本身)

迭代器給我們提供了不依賴於索引取值的方式
"""

# 迭代器物件
i = 12  # 沒有
f = 11.11  # 沒有
s = 'jason'  # 有
l = [111,222,333,444]  # 有
d = {'username':'jason','pwd':123}  # 有
t = (11,22,33)  # 有
se = {11,22,33}  # 有
b = True  # 沒有
file = open(r'a.txt','w',encoding='utf8') # 有

# res = s.__iter__()  # 轉成迭代器物件
# print(res.__next__())  # 迭代器物件執行__next__方法其實就是在迭代取值(for迴圈)
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())
# print(res.__next__())


# res = d.__iter__()  # 轉成迭代器物件
# print(res.__next__())  # 迭代器物件執行__next__方法其實就是在迭代取值(for迴圈)
# print(res.__next__())
# print(res.__next__())  # 取完元素之後再取會"報錯"

# 易錯
# print(d.__iter__().__next__())  # username
# print(d.__iter__().__next__())  # username
# print(d.__iter__().__next__())  # username
# print(d.__iter__().__next__())  # username
# print(d.__iter__().__next__())  # username
# print(d.__iter__().__next__())  # username
# 每次都是取第一個元素,並沒有迭代去取下一個元素,所以不會報錯

for迴圈的本質

l1 = [1,2,3,4,5,6,7,8,9,11,22,33,44,55]
# 迴圈打印出列表中每個元素 但是不能使用for迴圈  __next__()  next()
# 1.先將列表轉為迭代器物件
# res = l1.__iter__()
# # 2.迴圈執行__next__取值
# while True:
#     print(res.__next__())

# for i in l1:
#     print(i)
"""
for迴圈內部原理
    1.將關鍵字in後面的資料先呼叫__iter__方法轉為迭代器物件
    2.迴圈執行__next__方法
    3.取完之後__next__會報錯 但是for迴圈會自動捕獲該錯誤並處理

res = 資料.__iter__()
while True:
    檢測程式碼是否會報錯
    res.__next__()
    如果報錯了則自動處理掉並結束while迴圈
"""

異常捕獲

# 什麼是異常
	程式碼執行出錯會導致異常 異常發生後如果沒有解決方案則會到底整個程式結束
 
# 異常三個重要組成部分
	1.traceback
    	翻到最下面從下往上的第一個藍色字型滑鼠左鍵點選即可跳轉到錯誤的程式碼所在的行
    2.XXXError
    	錯誤的型別
    3.錯誤型別冒號後面的內容
    	錯誤的詳細原因(很重要 仔細看完之後可能就會找到解決的方法)

# 錯誤的種類   
    1.語法錯誤
    	不被允許的 出現了應該立刻修改!!!
    2.邏輯錯誤
    	可以被允許的 出現了之後儘快修改即可
   		'''修改邏輯錯誤的過程其實就是在從頭到尾理清思路的過程'''
        
# print(idna)  # NameError
# l = [11,22,33]
# print(l[100])  # IndexError
# d = {'username':'jason'}
# print(d['xxx'])  # KeyError
# int('abc')  # ValueError

"""
基本語法結構
    try:
        有可能會出錯的程式碼
    except 錯誤型別 as e:
        出錯之後對應的處理機制(e是錯誤的詳細資訊)
    except 錯誤型別 as e:
        出錯之後對應的處理機制(e是錯誤的詳細資訊)
    except 錯誤型別 as e:
        出錯之後對應的處理機制(e是錯誤的詳細資訊)
"""
# try:
#     int('abc')
# except NameError as e:
#     print('變數名name不存在',e)
# except ValueError:
#     print('值不是純數字')

# 萬能異常
try:
    # int('abc')
    print(name)
    # l = [11]
    # l[100]
except Exception:
    print('你來啥都行 無所謂')

"""
異常捕獲句式和萬能異常
    1.有可能會出現錯誤的程式碼才需要被監測
    2.被監測的程式碼一定要越少越好
    3.異常捕獲使用頻率越低越好
"""

END~