1. 程式人生 > >第16天 匿名函數,遞歸,二分法,內置函數

第16天 匿名函數,遞歸,二分法,內置函數

結束 出錯 條件 operation 個人 都是 需要 red 標準

匿名函數

1. 什麽是匿名函數?

匿名函數就是用lambda關鍵字聲明的一行沒有名字的函數。既然有匿名函數,就肯定有有名函數,有名函數就是通過def關鍵字聲明的有名字的函數。

2. 為什麽要用匿名函數呢?

匿名函數的特點就是沒有名字,不像有名函數,我們沒有辦法通過名字進行函數調用,只能在定義函數的階段就調用調用函數,這也就決定了這樣的函數只能被調用一次這個特點。(註意此處的調用一次只是說只能被一個時間調用一次)不僅如此,上面也說了匿名函數是通過lambda關鍵字聲明的一行函數,這也就決定了匿名函數只能做一些比較簡單的事情,並不能像有名函數一樣做復雜的事情。其實就算沒有匿名函數,有名函數也是可以解決所有的事情的,只是說匿名在一定的場合內使用會相對來說更為簡便。

3. 怎麽定義一個匿名函數呢?

#  lambda [parameter] ,[parameter] : expression
#  不需要return,直接: 後面的就是返回值
#  不需要名字,這也是匿名函數的特點
lambda x, y: x + y 

匿名函數的主要是被當內置函數的一些參數傳遞進來做一些簡單的操作的。接下來我們就來講一下內置函數究竟是怎麽使用匿名函數的

內置函數

max求最大值,並不會改變原有的值,只是通過max返回一個最大值(類型為字符串)

技術分享圖片
# max函數,從名字我們可以看出來求最大值
# 簡單的處理列表和字符串,求出
test = [1, 2, 3, 4, 5, 6]
s 
= abcdefg print(max(test), max(s)) print(test, s) # 結果: # 6 g # [1, 2, 3, 4, 5, 6] abcdefg
簡單的處理列表和字符串

首先我們來分析一下max的機制。

max的返回值和比較值是可以不一樣的(是通過key關鍵字來定義比較值的)。如下例的過程:

1. salaries是一個可叠代對象,當我們用了max之後,首先會執行iter內置函數把它變成一個叠代器對象,然後通過next函數一個一個的取出salaries的key,

2. 通過key等於一個匿名函數告訴max現在我不用salaries的key進行比較了,用這個函數的返回值作為標準比較吧,這樣就出現了返回值和比較值不一樣的情況,但是註意此處的key作為參數接受的其實是salaries字典的key,然後返回一個value作為比較對象進行的操作。

# 需求: 取出人名(要求人名的工資是最大的)
# 從需求中我們會發現我們要得到的是字典的key,但是比較的卻是字典的value
salaries={
    egon:3000,
    alex:100000000,
    wupeiqi:10000,
    yuanhao:2000
}
res = max(salaries, key=lambda key: salaries[key])
print(res)

# 結果:
# alex

min函數和max是一樣的,參照上面的操作就可以!

技術分享圖片
# 需求: 取出人名(要求人名的工資是最大的)
# 從需求中我們會發現我們要得到的是字典的key,但是比較的卻是字典的value
salaries={
    egon:3000,
    alex:100000000,
    wupeiqi:10000,
    yuanhao:2000
}
res = min(salaries, key=lambda key: salaries[key])
print(res)

# 結果:
# yuanhao
min函數的操作例子

sorted排序函數,和max是一樣的,只是多了一個參數reverse, 默認是Flase, 當設置reverse=True的時候會從大到小進行排序

技術分享圖片
# 需求: 取出人名(要求人名的工資是最大的)
# 從需求中我們會發現我們要得到的是字典的key,但是比較的卻是字典的value
salaries={
    egon:3000,
    alex:100000000,
    wupeiqi:10000,
    yuanhao:2000
}
# res = min(salaries, key=lambda key: salaries[key])
# 默認是從小到大金星排序的
res = sorted(salaries, key=lambda key: salaries[key])
# reverse=True,從大到小進行排序
res1 = sorted(salaries, key=lambda key: salaries[key], reverse=True)
print(res, res1)

# 結果:
# [‘yuanhao‘, ‘egon‘, ‘wupeiqi‘, ‘alex‘] [‘alex‘, ‘wupeiqi‘, ‘egon‘, ‘yuanhao‘]
sorted函數的例子

map映射,就是把第二個參數內的元素當作參數傳遞給參數一然後把返回值添加到一個叠代器的過程,需要註意的是python3會返回一個叠代器,但是python2會直接返回一個列表,同樣不會改變原來的值!

# 需求,在列表的每個字符後面加上‘_dsb‘
lists = [y)uanhao, egon, wupeiqi, alex]
# 用列表生成式來表示
new_lists = [item + _dsb for item in lists]
# 用map來表示,只有兩個參數,其實也就是把lists內的元素當前前面的參數傳遞進去之後返回給新的值
res = map(lambda item: item + _dsb, lists)
# 需要註意的是在python3中的返回值是一個叠代器,需要通過list轉換之後才能夠打印出來
print(res, list(res))

# 結果:
# <map object at 0x03C24950> [‘y)uanhao_dsb‘, ‘egon_dsb‘, ‘wupeiqi_dsb‘, ‘alex_dsb‘]

filter過濾,和map函數很相像,只是說filter可以進行判斷了,但是它只能過濾信息,不能返回一個修改的信息

# 把後綴沒有_dsb的人員名單挑出來
test = [yuanhao_dsb, egon_dsb, wupeiqi_dsb, alex_dsb, egon]
# 列表生成式來實現此功能
new_test = [item for item in test if not item.endswith(_dsb)]
# 用fliter函數來實現此功能
res = filter(lambda item: not item.endswith(_dsb), test)
print(res, list(res))
# 結果:
# <filter object at 0x03A348B0> [‘egon‘]

reduce組合,主要註意的是python3需要導入才能使用,

# 求1-100的和
# 這第三個參數是初始值,如果沒有,則會從叠代對象中找到一個值賦值給它
from functools import reduce
res = reduce(lambda x, y: x + y, range(100), 100)
print(res, type(res))

# 拼接字符串
test = [yuanhao_dsb, egon_dsb, wupeiqi_dsb, alex_dsb, egon]
from functools import reduce
res = reduce(lambda x, y: x + y, test, 10000)
print(res, type(res))

遞歸函數:

1. 什麽是遞歸函數?

函數的嵌套分為函數的嵌套定義和函數的嵌套調用,其實遞歸函數就是一種特殊的函數嵌套調用!特殊之處在與它每次的嵌套調用直接或者間接的都是它本身!

# 直接
def f1():
    f1()
    f2()
# 間接
def f2():
    f1()

函數的遞歸要滿足兩個條件

遞歸的兩個法則:
        1. 每次進入更深一層的遞歸調用的時候,問題的規模相比於上次都應該有所減少
        2. 應該有一個明確的結束條件或者說應該有一個條件規定進入更深層次的遞歸!

函數的遞歸的兩個階段

遞歸的兩個階段:
        1.回溯:從外層函數一層一層往裏面進行調用函數的過程。(在此階段應該有一個明確的結束遞歸標誌。)
        2.遞推:收到一個明確的停止遞歸的信息時,從此階段開始一層一層往外推導。

2. 為什麽要用遞歸函數?

遞歸函數從本質上來講就是一個循環,用來循環某一塊代碼塊。目前我們知道的循環有while,for以及遞歸,(for主要是用來取值操作的,和循環代碼塊還是不一樣的。)其實用while循環是肯定能夠實現遞歸作用的,只是說可能在某些時候遞歸函數可能會更加的簡便一些。

3. 怎麽使用遞歸函數?

例一:. 年齡的問題

# 需求:
‘‘‘
# 有五個人:問第一個人年齡的時候,它說比第二個人小2歲,問第二個人的時候
# 他說他比第三個人小兩歲,依次類推,問到第五個人的時候,他說他的年齡為18
# 求第一個人的年齡
‘‘‘
技術分享圖片
# 分析:
‘‘‘
age(1) = age(2) + 2 
age(2) = age(3) + 2 
age(3) = age(4) + 2 
age(4) = age(5) + 2
到第五個人的時候直接給我了年齡
這個點就是我們上面所說的明確的結束條件
age(5) = 18
‘‘‘
def age(n):
    if n == 5:
        return 18
    return age(n + 1) + 2
res = age(1)
print(res)
實現過程

例二:循環列表的問題

# 需求
# 有一個嵌套列表,打印出裏面的內容
# lists = [1,[2,[3,4,[5,[6,[7,[8]]]]]]]
技術分享圖片
# 分析
lists = [1,[2,[3,4,[5,[6,[7,[8]]]]]]]
def show_list(lists):
    for item in lists:
        # 此處就是我們上面說的進入更深層次的遞歸條件
        # 不僅如此,每次進入更深層次的遞歸都會問題的規模都會減小,就像剝洋蔥一樣
        # 總歸有剝完的那一刻
        if type(item) == list:
            # 當判斷是列表之後,遞歸調用顯示
            show_list(item)
        else:
            print(item, end=‘‘)
show_list(lists)
實現過程

例三:二分法的問題

# 需求,給定一個值,確定是否在列表中
# 這個通過in都是可以解決的,但是我們需要自己取實現這樣的功能
nums = [1, 2, 3, 5, 90, 91,92,1002, 2302, 2829, 203920]
技術分享圖片
# 需求,給定一個值,確定是否在列表中
nums = [1, 2, 3, 5, 90, 91,92,1002, 2302, 2829, 203920]

def binary_search(find_num, nums):
    ‘‘‘
    從一個有序列表中找到我們需要找的值
    :param find_num: 是我們需要找的值
    :param nums: 一個有序列表
    :return:
    ‘‘‘
    print(nums)
    # 首先判斷是否是空列表,如果是則直接返回沒有找到對應的數據
    if not nums:
        print(沒有找到!)
        return
    # 如果列表不為空,先找到有序列表的中間索引位置
    mid_index = len(nums) // 2
    # 直接等於中間的位置的數,不需要再遞歸調用了
    # 這也是我們經常說的有一個進入遞歸的條件
    if nums[mid_index] == find_num:
        print(數據在這個列表中!)
    # 找的值是在中間值得右邊
    elif nums[mid_index] < find_num:
        # 重新構造一個列表調用遞歸
        # 也就是我們問題的規模在減小
        binary_search(find_num, nums[mid_index + 1:])
    # 找的值是在中間值得左邊
    else:
        binary_search(find_num, nums[:mid_index])


find_num = -1
binary_search(find_num, nums)


# 結果:從結果中我們就可以看到規模在不斷的減小
‘‘‘
[1, 2, 3, 5, 90, 91, 92, 1002, 2302, 2829, 203920]
[1, 2, 3, 5, 90]
[1, 2]
[1]
[]
沒有找到!
‘‘‘
實現過程

作業:

1. 將names=[‘egon‘,‘alex_sb‘,‘wupeiqi‘,‘yuanhao‘]中的名字全部變大寫

# 常規方法
names = [egon, alex_sb, wupeiqi, yuanhao]
for i in range(len(names)):
    names[i] = names[i].upper()
print(names)
# 列表生成式
names = [names[i].upper() for i in range(len(names))]
print(names)
# map函數
names = map(lambda key: key.upper(), names)
print(list(names))

2.將names=[‘egon‘,‘alex_sb‘,‘wupeiqi‘,‘yuanhao‘]中以sb結尾的名字過濾掉,然後保存剩下的名字長度

names=[egon,alex_sb,wupeiqi,yuanhao]
# 常規方法
new_names = []
for name in names:
    if not name.endswith(sb):
        new_names.append(len(name))
names = new_names
print(names)


# 列表表達式
names = [len(name) for name in names if not name.endswith(sb)]
print(names)


# 用fiter函數,因為它要返回的是列表的內容的長度,因此只用filter是完成不了的
res = map(lambda key: len(key), filter(lambda key: not key.endswith(sb), names))
print(list(res))


# 結果:
# [4, 7, 7]

3. 求文件a.txt中最長的行的長度(長度按字符個數算,需要使用max函數)

文件的內容(隨便寫的內容):

技術分享圖片
lsjegl
ljege  lsje eglh胡

了就掛了就掛了
ljsleg

lsjs
ls


lsjeg;agja;hhsljeg
a.txt文件內容
# 常規方法
with open(a.txt, rt, encoding=utf-8) as f:
    temp = 0
    for line in f:
        if temp < len(line):
            temp = len(line)
print(temp)


# 列表生成式
with open(a.txt, rt, encoding=utf-8) as f:
    res = max([len(line) for line in f])
print(res)

4. 求文件a.txt中總共包含的字符個數?思考為何在第一次之後的n次sum求和得到的結果為0?(需要使用sum函數)

with open(a.txt, rt, encoding=utf-8) as f:
    res = sum([len(line) for line in f])
print(res)

但是下面這中方法就會報錯:這種問題主要python3在一個列表生成式生成的並不是一個列表,而是一個叠代器對象,因此當我們使用sum(res)進行求和的時候,首先sum會去執行res這個叠代器的__next__去獲得值,但是叠代器裏面根本就沒有值,需要重新打開文件取值型裏面的操作,但是現在文件已經關閉了,所以會報在一個關閉的文件夾裏面進行i/o操作。

with open(a.txt, rt, encoding=utf-8) as f:
    res = (len(line) for line in f)
print(sum(res))


# 報錯信息:
  File "H:/python_study/day16/作業.py", line 70, in <module>
    print(sum(res))
  File "H:/python_study/day16/作業.py", line 69, in <genexpr>
    res = (len(line) for line in f)
ValueError: I/O operation on closed file.

如果改成下面這種情況就不會出錯:

f = open(a.txt, rt, encoding=utf-8)
res
= (len(line) for line in f)
# 當我需要值的時候通過叠代器去取值,此時文件並沒有關閉所以並不會報錯
print(sum(res)) f.close()

6. 文件shopping.txt內容如下

mac,20000,3
lenovo,3000,10
tesla,1000000,10
chicken,200,1
技術分享圖片
# 需求
# 求總共花了多少錢?

with open(shop.txt, rt, encoding=utf-8) as f:
    # 首先去除每行兩邊的空格和換行符再以,分割得到用戶信息列表
    shop_list = [line.strip( \n).split(,) for line in f]
    # 循環列表之後對相乘疊加
    res = sum(int(item[1]) * int(item[2]) for item in shop_list)
print(res)
求總共花了多少錢? 技術分享圖片
# 打印出所有商品的信息,格式為[{‘name‘:‘xxx‘,‘price‘:333,‘count‘:3},...]
lists = []
with open(shop.txt, rt, encoding=utf-8) as f:
    # 獲得每一行的值並添加到一個列表中
    items = [line.strip( \n) for line in f]
    # 循環一個列表,並把信息已拼接的字典形式添加進去
    for item in items:
        lists.append({
            name: item.split(,)[0],
            price: int(item.split(,)[1]),
            count: int(item.split(,)[2]),
        })
print(lists)
打印出所有商品的信息 技術分享圖片
# 求單價大於10000的商品信息,格式同上
lists = []
with open(shop.txt, rt, encoding=utf-8) as f:
    # 獲得每一行的值並添加到一個列表中
    items = [line.strip( \n) for line in f]
    # 循環一個列表,並把信息已拼接的字典形式添加進去
    for item in items:
        lists.append({
            name: item.split(,)[0],
            price: int(item.split(,)[1]),
            count: int(item.split(,)[2]),
        })
res = filter(lambda item_dict: item_dict[price] > 10000, lists)
print(list(res))
求單價大於10000的商品信息

7. 實現類似與in的功能

技術分享圖片
# 實現類似於in的功能
l = [1,2,10,33,30,99,101,200,301,311,402,403,500,900,1000]

def search(find_num, l):
    l = sorted(l)
    print(l)
    if not l:
        print(不在列表中!)
        return False
    mid_index = len(l) // 2
    if l[mid_index] == find_num:
        print(在列表中!)
        return True
    elif l[mid_index] < find_num:
        search(find_num, l[mid_index + 1:])
    else:
        search(find_num, l[:mid_index])
search(3, l)
實現類似於in的功能

8.實現類似於l.index(30)的效果

技術分享圖片
#實現類似於l.index(30)的效果
l = [1,2,10,33,30,99,101,200,301,311,402,403,500,900,1000]

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
實現類似於l.index(30)的效果

第16天 匿名函數,遞歸,二分法,內置函數