首页 > 技术文章 > 递归函数,二分法,三元表达式,生成式,匿名函数,匿名函数常用配置函数

gengfenglog 2020-12-30 19:28 原文

递归函数

digui1_gaitubao_607x266

函数的递归调用

递归:函数在运行过程中直接或者间接的调用了自身

大前提:递归调用一定要有明确的结束条件

def func():
    print("func")
    func()  # 直接调用本身

func()

'''
调用函数会产生局部的名称空间占用内存,上述这种调用会无限调用本身,python解释器的
内存管理机制为了防止其无限制占用内存,对函数的递归调用做了最大的层级限制,因而不会像想
象中那样进入无限循环,会抛出异常,要避免此类情况就必须让递归调用在满足某个特点条件下终止
官网表示:python默认的最大递归深度为1000次
'''
# 可以修改递归最大深度
import sys
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())

'''
虽然可以设置,但不可能无限递归,而且无限制地递归调用本身是毫无意义的
递归应该分为两个明确的阶段,回溯与递推
'''

递归的两个阶段

递归调用应该包含两个明确的阶段:递推,回溯

  • 1.递推
    • 一层层往下推导答案(每次递归之后复制度相较于上一次一定要有所下降)
  • 2.回溯
    • 依据最后的结论往回推导出最初需要的答案( 回溯阶段必须要有一个明确的结束条件)
# 示例1:第五个小朋友到底有多少颗糖果
'''
有五个小朋友坐在一起,问第五个小朋友有多少颗糖果,他说比第四个小朋友多2颗,
问第四个小朋友,他说比第三个小朋友多2颗,问第三个小朋友,他说比第二个小朋友多2颗,
问第二个小朋友,他说比第一个小朋友多2颗,最后问第一个小朋友,他说有10颗糖

# 伪代码:即数学表达式,可能无法运行,但是可以表述逻辑
candy(5) = candy(4) + 2
candy(4) = candy(3) + 2
candy(3) = candy(2) + 2
candy(2) = candy(1) + 2
candy(1) = 10
'''
def get_candy(n):
    if n == 1:
        return 10
    return get_candy(n - 1) + 2
  
print(get_candy(5))  # 18

diguihuishu

# 示例2:打印出列表中每一个元素(列表除外)
l = [1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,]]]]]]]]]]]]]]
'''
思路:
1.循环该列表,获取列表内每一个元素
2.判断该元素是否是数字,如果是数字则直接打印
3.如果是列表则循环该列表,获取列表内每一个元素
4.判断该元素是否是数字,如果是数字则直接打印
5.如果是列表则循环该列表,获取列表内每一个元素
'''
def get_num(l):
    for i in l:
        if type(i) is int:  # 判断是数字打印数字
            print(i)
        else:
            # 是列表继续做for循环,然后判断
            get_num(i)
            
get_num(l)  # for循环完自动结束,不用给结束条件

WechatIMG577_gaitubao_493x232


二分法

想从一个按照从小到大排列的数字列表中找到指定的数字,遍历的效率太低,用二分法(算法的一种,算法是解决问题的高效方法)可以极大的缩小问题规模

  • 二分法能够使用的场景:数据集必须有序(升序降序都可以)
# 二分法(入门级别:还有一定距离)
# 找到列表中元素为321的数字 
l = [11, 23, 43, 57, 68, 76, 81, 99, 123, 321, 432, 567, 666, 712, 899, 999, 1111]
  
  
# 第一种方式:直接for循环从左往右依次查找,如果该列表元素过万,效率是不是很低

# 第二种方式:二分法

def my_partner(target_num, l):  # target_num=321  l=l
    if len(l) == 0:
        print('不好意思 我尽力 没找到')
        return
    # 先获取中间位置索引值
    middle_index = len(l) // 2  # 8
    # 判断中间索引对应的值比目标值大还是小
    if target_num > l[middle_index]:
        # 说明要找的元素只可能出现在列表的右侧,切分顾头不顾尾需+1
        l_right = l[middle_index + 1:]  # l[9:]
        # print(l_right)
        my_partner(target_num, l_right)
    elif target_num < l[middle_index]:
        # 说明要找的元素只可能出现在列表的左侧
        l_left = l[:middle_index]
        # print(l_left)
        my_partner(target_num, l_left)
    else:
        print('找到了', target_num)
        
my_partner(321, l)  # 找到了 321        
# my_partner(444, l)  # 找不到 需要添加结束条件
# my_partner(11, l)  # 要查找的元素在开头,那么还没有依次查找的效率高

'''
二分法查找,也称为折半法,是一种在有序列表中查找特定元素的搜索算法。 二分法查找的思路如下:
    (1)首先,从列表的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
    (2)如果目标元素大于/小于中间元素,则在列表大于/小于中间元素的那一半区域查找,然后重复步骤的操作。
    (3)如果某一步列表为空,则表示找不到目标元素。
'''



WechatIMG579_gaitubao_369x222


三元表达式

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

print(my_max(3, 4))  # 4

"""
当功能需求仅仅是二选一的情况下,那么推荐使用三元表达式
"""
def my_max(a, b):
    return a if a > b else b
print(my_max(3, 4))  # 4

"""
条件成立采用if前面的值 if 条件 else 条件不成立采用else后面的值
三元表达式尽量不要嵌套使用
"""
res = 'ok' if 10 > 2 else 'no'
print(res)  # ok

# 嵌套使用
res = '不干饭' if 3 > 5 else ('去干饭' if 2 > 5 else '自己做饭!')
print(res)  # 自己做饭!

is_free = input('电影是否收费(y/n)>>>:').strip()
print('收费' if is_free == 'y' else '免费')  # 收费

zhenxiang


生成式

列表生成式

# 例1:将0-9的数字加入列表
l = [] # 定义一个列表
'''传统做法'''
for i in range(10):
    l.append(i)  
print(l)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

'''列表生成式'''
l = [i for i in range(10)]  # 用一行代码搞定
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# 例2:将0-9中大于5的数字加入列表
l = []
'''传统做法'''
for i in range(10):
    if i > 5:
        l.append(i)
print(l)  # [6, 7, 8, 9]

'''列表生成式'''
# for循环里面它的子代码块是一个if判断,每循环一次判断一次条件,条件成立运行左边的表达式将结果放入列表中
l = [i for i in range(10) if i > 5]
print(l)  # [6, 7, 8, 9]


# 例3:给列表中所有的人名加上_DSB后缀
name_list = ['jason', 'kevin', 'tony', 'jerry']
# 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)  # ['jason_DSB', 'kevin_DSB', 'tony_DSB', 'jerry_DSB']

'''列表生成式'''
res = ['%s_DSB' % name for name in name_list]
print(res)  # ['jason_DSB', 'kevin_DSB', 'tony_DSB', 'jerry_DSB']


# 例4:过滤出末尾带DSB的名单
name_list = ['jason', 'kevin_DSB', 'tony_DSB', 'jerry_DSB']
# 1.定义一个空列表
new_list = []
'''传统做法'''
# 2.for循环老列表
for name in name_list:
  	# 3.判断名字是否以DSB结尾
    if name.endswith('DSB'):
        # 4.添加到新的列表中
        new_list.append(name)
print(new_list)

'''列表生成式'''
res = [name for name in name_list if name.endswith("DSB")]
print(res)  # ['kevin_DSB', 'tony_DSB', 'jerry_DSB']

字典生成式

# 例1:生成key: value为key的平方
res = {i: i ** 2 for i in range(5)}
print(res)  # {0:0, 1:1, 2:4, 3:9, 4:16}


# 例2:将列表生成字典
items = [('k1',111),('k2',222),('k3',333)]
# 直接转
print(dict(items)) 

# 用字典生成式转
print({k:v for k,v in items})  # {'k1': 111, 'k2': 222, 'k3': 333}


# 例3:将列表生成字典
l1 = ['name', 'age', 'hobby']
l2 = ['jason', 18, 'read']
# 1.定义一个空字典
new_dict = {}
# 2.for循环拿到l1的索引值
for i in range(len(l1)):
    new_dict[l1[i]] = l2[i]
    
print(new_dict)  # {'name': 'jason', 'age': 18, 'hobby': 'read'}

# 枚举
'''
enumerate(l1)
    针对该方法使用for循环取值 每次会产生两个结果
        第一个是从0开始的数字
        第二个是被循环对象里面的元素
    还可以通过start参数控制起始位置
'''
for i, j in enumerate(l1, start=1):  # start=1 不指定从0开始
     print(i, j)
'''
1 name
2 age
3 hobby
'''

# 例4:将列表里面的元素生成字典的值
name_list = ['jason', 'kevin', 'tony', 'jerry']
res = {i: j for i, j in enumerate(name_list)}
print(res)  # {0: 'jason', 1: 'kevin', 2: 'tony', 3: 'jerry'}

# 可以跟if条件
res = {i: j for i, j in enumerate(name_list) if j != 'jason'}
print(res)  # {1: 'kevin', 2: 'tony', 3: 'jerry'}

集合生成式

# 例1:不要value只写个key 就是集合生成式
res = {i for i in range(5)}
print(res)  # {0, 1, 2, 3, 4}

# 例2:
name_list = ['jason', 'kevin', 'tony', 'jerry']
res = {i for i, j in enumerate(name_list)}
print(res,type(res))  # {0, 1, 2, 3} <class 'set'>


# 迭代器
res1 = (i for i,j in enumerate(name_list))
print(res1)  # <generator object <genexpr> at 0x7ff3a88d0900>

buoei_gaitubao_295x249


匿名函数

匿名函数:没有名字的函数

  • 有名函数与匿名函数的对比

    • 有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
    • 匿名函数:一次性使用,随时定义
def index():  # 自定义的函数一次定义可以反复调用
    pass

print(index)  # <function index at 0x7fa39fab91f0>

"""
语法格式
    lambda 形参:返回值
"""
f = lambda x, y: x + y
print(f)  # <function <lambda> at 0x7fe04a908550>
res = f(1, 2)
print(res)  # 3

res = (lambda x, y: x + y)(1, 2)
print(res)  # 3

print((lambda x: x ** 2)(2))  # 4
res = lambda x: x ** 2
print(res(2))  # 4

匿名函数配置使用的常见函数

  • 匿名函数一般不会单独使用,都是配合其他函数一起使用

  • lambda匿名函数的应用:map,max,min,sorted,reduce,filter
# map() 映射
# map在python2中是列表,在python中是老母鸡
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, 4, 9, 16, 25, 36, 49, 64, 81]


# zip() 拉链
l = [11, 22, 33, 44]
name_list = ['jason', 'kevin', 'tony', 'jerry']
# 方式一:
new_list = []
for i in range(len(l)):
    new_list.append((l[i], name_list[i]))

print(new_list)  # [(11, 'jason'), (22, 'kevin'), (33, 'tony'), (44, 'jerry')]

# 方式二:使用zip
l1 = [1, 2, 3, 4]
l2 = [4, 3, 2, 1]
res = zip(l, name_list, l1, l2)
print(list(res))  
# [(11, 'jason', 1, 4), (22, 'kevin', 2, 3), (33, 'tony', 3, 2), (44, 'jerry', 4, 1)]


# max()最大值,min()最小值,sorted()排序
salaries = {
    "jason": 3000,
    "kiven": 1000000,
    "tony": 1000
}
# 打印列表中最大的元素
print(max([11, 22, 33]))  # 33
# 打印出最高薪资
print(max(salaries.values()))  # 1000000
# 未指定比较大小的条件,其实比较的是k
print(max(salaries))  # tony

# max用完该函数就没用了
def func(k):  # 该函数是由max功能引发的
    return salaries[k]

# 打印薪资最高的人名  
print(max(salaries, key=func))  # kiven
# 匿名函数的形式
print(max(salaries, key=lambda k: salaries[k]))  # kiven 
# 打印薪资最低的人名 
print(min(salaries, key=lambda k: salaries[k]))  # tony
# 薪资按照从小到大排序
print(sorted(salaries, key=lambda k: salaries[k]))  # ['tony', 'jason', 'kiven'] 
# 薪资按照从大到小排序
print(sorted(salaries, key=lambda k: salaries[k],reverse=True))  # ['kiven', 'jason', 'tony']


# reduce函数:合并
# reduce在python2中是内置函数,
from functools import reduce

res = reduce(lambda x,y:x+y,[1,2,3])
print(res)  # 6
res1 = reduce(lambda x,y:x+y,["aa","bb","cc"])
print(res1)  # aabbcc


# filter函数:过滤
# 过滤出sb
names = ["lxx_sb", 'jason', "wxx_sb", 'lili_sb']
# 方式1
print([name for name in names if name.endswith('sb')])
# 方式2
res = filter(lambda name:name.endswith('sb'),names)
print(list(res))  # ['lxx_sb', 'wxx_sb', 'lili_sb']

# 过滤出大于30的元素
l = [11, 22, 33, 44, 55]
res = filter(lambda x: x > 30, l)
print(list(res))  # [33, 44, 55]

推荐阅读