匿名函数

1. 什么是匿名函数?

匿名函数就是用lambda关键字声明的一行没有名字的函数。既然有匿名函数,就肯定有有名函数,有名函数就是通过def关键字声明的有名字的函数。

2. 为什么要用匿名函数呢?

匿名函数的特点就是没有名字,不像有名函数,我们没有办法通过名字进行函数调用,只能在定义函数的阶段就调用调用函数,这也就决定了这样的函数只能被调用一次这个特点。(注意此处的调用一次只是说只能被一个时间调用一次)不仅如此,上面也说了匿名函数是通过lambda关键字声明的一行函数,这也就决定了匿名函数只能做一些比较简单的事情,并不能像有名函数一样做复杂的事情。其实就算没有匿名函数,有名函数也是可以解决所有的事情的,只是说匿名在一定的场合内使用会相对来说更为简便。

3. 怎么定义一个匿名函数呢?

#  lambda [parameter] ,[parameter] : expression
#  不需要return,直接: 后面的就是返回值
#  不需要名字,这也是匿名函数的特点
lambda x, y: x + y 

匿名函数的主要是被当内置函数的一些参数传递进来做一些简单的操作的。接下来我们就来讲一下内置函数究竟是怎么使用匿名函数的

 

内置函数

max求最大值,并不会改变原有的值,只是通过max返回一个最大值(类型为字符串)

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 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是一样的,参照上面的操作就可以!

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 需求: 取出人名(要求人名的工资是最大的)
# 从需求中我们会发现我们要得到的是字典的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的时候会从大到小进行排序

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 需求: 取出人名(要求人名的工资是最大的)
# 从需求中我们会发现我们要得到的是字典的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
# 求第一个人的年龄
'''
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 分析:
'''
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]]]]]]]
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 分析
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]
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 需求,给定一个值,确定是否在列表中
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函数)

文件的内容(随便写的内容):

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
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
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 需求
# 求总共花了多少钱?

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)
求总共花了多少钱?
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 打印出所有商品的信息,格式为[{'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)
打印出所有商品的信息
第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 求单价大于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的功能

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
# 实现类似于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)的效果

第16天 匿名函数,递归,二分法,内置函数-LMLPHP第16天 匿名函数,递归,二分法,内置函数-LMLPHP
#实现类似于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)的效果
10-10 22:20