7.12函数(四)

一、函数递归

1.什么是递归

函数在调用阶段直接或者间接的又调用了自身

(1)直接

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

死循环,无限调用函数

(2)间接

def index():
    print('from index')
    login()

def login():
    print('from login')
    index()
index()

也是死循环,无限调用这两个函数,不是在自身函数中直接调用,而是间接调用

2.递归深度

Python为了避免函数无限递归下去,设置了一个函数递归深度(重复执行上限),避免函数无限递归,导致计算机内存空间溢出(发生卡死,死机)。

(1)如何查看递归深度

import sys  # 调用模块
print(sys.getrecursionlimit())  # 查看递归深度

递归深度(重复调用)默认是1000,不过不是很准确

def func(n):
    print('from func',n)
    func(n+1)
func(1)  # 精确一点的python中最大递归深度是998左右

精确一点的python中最大递归深度是998左右

(2)如何修改最大递归深度

import sys  # 调用模块
sys.setrecursionlimit(2000)  # 修改递归深度
def func(n):
    print('from func',n)
    func(n+1)
func(1) 

结论:函数不应该无限制的递归下去

3.递归用在什么地方

  如果让函数无限制的递归下去没有任何意义,那递归应该用在什么场景?

举个例子:

  询问A的年龄,A说比B小两岁,又问B,B说比C小两岁,再问C,C说比D小两岁......,直到E说他26岁,一个个推导得出A18岁

递归分为两个阶段:

  (1)回溯:就是一次次重复询问的过程,这个重复的过程必须建立在每一次重复问题的复杂度都应该下降,直到有一个最终的结束条件(就是E说出26岁)

  (2)递推:一次次往回推导的过程(推导出A18岁)

用代码演示这个例子:

'''
age(A) = 18
age(B) = age(A) + 2
age(C) = age(B) + 2
age(D) = age(C) + 2
age(E) = age(D) + 2
age(1) = 18
age(2) = age(1)+2
age(n) = age(n-1)+2  # n>1
'''

def age(n):
    if n == 1:  # 必须要有结束条件
        return 18  # 返回18岁
    else:
        return age(n-1) + 2
res = age(5)
print(res)
View Code

如何做到:

  将列表中的数字依次打印出来:l = [1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,]]]]]]]]]]]]]

推导思路:

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:
                        ...  # 无限重复,前提还得知道有多少层列表

如果不知道多少层列表怎样做

l = [1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,]]]]]]]]]]]]]
def get_num(l):
    for i in l:
        if type(i) is int:  # 如果是整型就打印
            print(i)
        else:  # 否则再循环这个列表
            get_num(i)
get_num(l)

结论:递归函数不需要考虑循环的次数,只需要把握结束的条件即可

二、算法之二分法

1.什么是算法

  解决问题的高效率的方法

2.如何实现:

  找到num=345是不是在 l = [1,3,5,12,57,89,101,123,146,167,179,189,345]列表中

l = [1,3,5,12,57,89,101,123,146,167,179,189,345]
num = 345
for i in l:
    if num == i:
        print('找到了')

如果这个列表有非常多数据,那查找效率就会变低,算法就是提高效率的方法,比如这里可以用二分法

3.二分法

  先在列表中间找,然后判断出在哪一边,然后在那一边的中间再找,没找到就再重复上述操作,提高查找效率

l = [1,3,5,12,57,89,101,123,146,167,179,189,345]
def get_num(l,target_num):
    print(l)  # 把每次切取到的列表打印下
    if not l:  # 判断列表是否为空了,空了还没找到就说明不在列表中
        print('你要找的值不在列表中')
        return
    middle_index = len(l) // 2  # 获取列表中间的索引
    if target_num > l[middle_index]:  # 判断target_num跟middle_index对应的数字的大小
        num_right = l[middle_index + 1:]  # 切取列表右半部分
        get_num(num_right,target_num)  # 再递归调用get_num函数,传入的列表变成切取的新列表
    elif target_num < l[middle_index]:  # 判断target_num跟middle_index对应的数字的大小
        num_left = l[0:middle_index]  # 切取列表左半部分
        get_num(num_left, target_num)  # 再递归调用get_num函数,传入的列表变成切取的新列表
    else:  # target_num = l[middle_index]的情况
        print('找到了')
get_num(l,345)
get_num(l,666)

使用二分法有一个前提:

容器类型里面的数字必须有大小顺序,没有顺序可以使用 sort() 进行排序

三、三元表达式

1.比较大小的函数

def my_max(x,y):
    if x > y:
        return x
    else:
        return y
print(my_max(1,5))

当x大的时候返回x,当y大的时候返回y,也就是当某个条件成立做一件事,不成立做另外一件事

2.如何用三元表达式表示这个函数

x = 1
y = 5
res = x if x > y else y  # 如果if后面的条件成立返回if前面的值,否则返回else后面的值
print(res)

如果if后面的条件成立返回if前面的值,否则返回else后面的值

3.三元表达式的固定语法结构

值1 if 条件 else 值2

   条件成立返回值1

   条件不成立返回值2

4.三元表达式的应用场景

  三元表达式只推荐只有两种情况的可能下

例如:

is_free = input('请输入是否免费(y/n)>>>:')
is_free = '免费' if is_free == 'y' else '收费'
print(is_free)
username = input('username>>>:')
res = 'NB' if username == 'francis' else '垃圾'
print(res)

四、列表生成式

1.如何实现:

  使列表中的字符串后面加上字符 _NB:l = ['tank','nick','oscar','sean']

l = ['francis','jason','tank','egon']
l1 = []
for name in l:
    l1.append('%s_NB'%name)
    # l1.append(name + '_NB')  # 等价于上面的办法,不推荐使用,python中这种字符串拼接效率太低
print(l1)

如何简单实现,需要用到列表生成式

2.列表生成式

l = ['francis','jason','tank','egon']
res = ['%s_NB'%name for name in l]  # for循环依次拿出name交给for前面的,作为列表的一个个元素
print(res)

原理:for循环依次拿出name交给for前面的,作为列表的一个个元素

3.列表生成式还可以加上if判断

  取出列表中以 _NB结尾的元素:l = ['francis_NB','jason_NB','tank_NB','egon']

l = ['francis_NB','jason_NB','tank_NB','egon']
res = [name for name in l if name.endswith('_NB')]  # 后面不支持再加else的情况
# 先for循环依次取出列表里面的每一个元素
# 然后交由if判断,条件成立才会交给for前面的,作为列表的一个个元素
# 如果条件不成立,当前的元素,直接舍弃
print(res)

原理:先for循环依次取出列表里面的每一个元素,然后交由if判断,条件成立才会交给for前面的,作为列表的一个个元素,如果条件不成立,当前的元素,直接舍弃

五、字典生成式

1.如何实现:

  把两个列表的数值一一对应,组成一新个字典:l1 = ['name','password','hobby']、l2 = ['francis','123','read']

l1 = ['name','password','hobby']
l2 = ['francis','123','read']
d = {}
for k,v in enumerate(l1):  # v是新字典的key,k是l2值对应的索引
    d[v] = l2[k]
print(d)

2.字典生成式

l = ['francis','123','read']
d = {i:j for i,j in enumerate(l) if j != '123'}  # 还可以加上if判断,如果j不等于'123'就交个for前面的,否则舍弃
print(d)

3.集合生成式

s = {i for i in range(10) if i != 4}
print(s)

只要记住这个格式,外面加什么就生成什么(基本)

4.生成器表达式

t = (i for i in range(10) if i != 4)  # 这样写不是元组生成式,而是生成器表达式
print(t)  # 返回的是个老母猪
for i in t:
    print(i)

六、匿名函数

1.什么是匿名函数

  没有名字的函数(也就意味着之后无法通过函数名加括号调用),所以是临时存在的函数,用的时候才存在,用过就没了

2.怎么用

lambda x,y:x+y  # 求和的匿名函数

就等价于下面的有名函数

def my_sum(x,y):
    return x + y  # 求和的有名函数

lambda x,y:x+y

(1)冒号(:)左边的相当于函数的形参

(2)冒号(:)右边的相当于函数的返回值

(3)匿名函数通常不会单独使用,而是配合内置函数一起使用

(4)也可以加括号使用,不过用完就没了

res = (lambda x,y:x+y)(1,2)  # 加括号使用并传参数
print(res)

(5)也可以给匿名函数命名,命名知乎就变成有名函数了,但失去了匿名函数的意义

func = lambda x,y:x+y
print(func(1,2))  # 函数加括号调用

七、常用的内置函数

1.max、min(最大值,最小值)

  得出薪资最高的人的名字(比较薪资,返回人名):

  d = {'francis':30000, 'jason':20000, 'egon':15000, 'tank':10000}

(1)直接用max比较

d = {'francis':30000, 'jason':20000, 'egon':15000, 'tank':10000}
print(max(d))  # 比较的是字典的key的大小,得出tank,不对

比较的是字典的key的大小(字典暴露给用户的只有key)

(2)调用函数的办法

d = {'francis':30000, 'jason':20000, 'egon':15000, 'tank':10000}
def index(name):
    return d[name]
print(max(d,key=index))  # max支持传一个函数,这样传入的key就被函数index变成了value,比较的就是value了

这样index函数只是为下面的比较大小服务的,就可以简化成匿名函数

d = {'francis':30000, 'jason':20000, 'egon':15000, 'tank':10000}
print(max(d,key=lambda name:d[name]))  # 冒号左边是函数的形参,右边是返回值

得出薪资最低的人也是这样

d = {'francis':30000, 'jason':20000, 'egon':15000, 'tank':10000}
print(min(d,key=lambda name:d[name]))

2.map(映射)

  把列表中的每个数字都加1:l = [1,2,3,4,5,6]

l = [1,2,3,4,5,6]
res = map(lambda a:a+1,l)  # map是基于for循环的,返回的是一个老母猪
print(list(res))  # list会循环老母猪里的数值组成列表

3.zip(拉链)

  让列表中的值一一组合在一起:l1 = [1,2,3]、l2 = ['jason','egon','tank']、l3 = ['a','b','c']

l1 = [1,2,3,4]
l2 = ['jason','egon','tank']
l3 = ['a','b','c']
print(list(zip(l1,l2,l3)))  # 得出的是列表中包含一对对元组
# 如果某列表元素多于其他列表也不影响,只保留能对应上的部分

4.filter(过滤)

  返回一个新列表,去除3:l = [1,2,3,4,5,6]

l = [1,2,3,4,5,6]
res = filter(lambda x:x != 3,l) # filter是基于for循环的,返回的是一个老母猪
print(list(res))  # list会循环老母猪里的数值组成列表

5.sorted(排序)

  给列表排序:l = [3,5,1,4,2,4]

l = [3,5,1,6,2,4]
res = sorted(l)  # 相当于sort,默认升序
print(res)
res1 = sorted(l,reverse=True)  # 也可以传参数,变为降序
print(res1)

6.reduce

类似于一个工厂,你给我多份材料,我只返回一个结果(给我一个列表,只返回一个值)

  把列表中的值相加:l = [1,2,3,4,5,6]

(1)没有初始值的时候

l = [1,2,3,4,5,6]
from functools import reduce  # reduce不可以直接用,需要先导入一个模块
print(reduce(lambda x,y:x+y,l))  # 没有初始值
# 没有初始值的时候,第一次获取两个值然后相加,之后每次获取一个值与上一次相加的结果再相加

(2)有初始值的时候

l = [1,2,3,4,5,6]
from functools import reduce  # reduce不可以直接用,需要先导入一个模块
print(reduce(lambda x,y:x+y,l,10))  # 初始值为10,初始值就是第一个参数
# 有初始值的时候,第一次获取一个值然后相加,之后每次获取一个值与上一次相加的结果再相加
原文地址:https://www.cnblogs.com/francis1/p/11181995.html