5.3 Python 闭包函数,递归函数,匿名函数

1. 函数对象

Python中所有的数据都是对象, 函数的值也是一个对象, 通过函数名获取函数对象.
使用函数名也就是使用函数对象, 函数对象加上括号立刻执行函数体代码块.
函数名的其它作用:
* 1. 函数名可以作为其它变量的值.
* 2. 函数名可以作为其它函数的参数.
* 3. 函数名可以作为函数的返回值.
* 4. 函数名可以作为容器类型的的元素.
1.1 引用函数变量
# 检测代码, 申请内存地址存储函数体代码块, 并将函数体代码块的内存地址绑定给标识符func1.
def func1():
    print('1')


# func2 引用 func1的内存地址, 也就是函数体代码块的内存地址.
func2 = func1
# 加括号调用
func2()

1.2 做为参数
# 检测代码, 申请内存地址存储函数体代码块, 并将函数体代码块的内存地址绑定给标识符func1.
def func1():
    print('1')


def func2(func):
    # 调用func1函数
    func1()  # 1


# 函数名func1最为另一个函数的参数
func2(func1)

# 省略写法, 语句不明确, 不推荐.
def func1():
    print('1')


def func2():
    # 可以直接使用到外部的名称
    func1()


func2()

1.3 做为返回值
# 检测代码, 申请内存地址存储函数体代码块, 并将函数体代码块的内存地址绑定给标识符func1.
def func1():
    print('1')


def func2():
    # 直接可以使用到外部的名称
    return func1


func = func2()
func()  # 1

从外部范围隐藏名称xx.
Pycharm的提示需要重命名参数名称来消除这个警告.

2022-12-11_01679

# 取消从外部范围隐藏名称提示.
def func1():
    print('1')


def func2(fun_p):
    # 直接可以使用到外部的名称
    return fun_p


func = func2(func1)
func()  # 1

# 省略写法, 语句不明确, 不推荐.
def func1():
    print('1')


def func2():
    # 直接可以使用到外部的名称
    return func1


func = func2()
func()  # 1

1.4 做为容器元素
# 检测代码, 申请内存地址存储函数体代码块, 并将函数体代码块的内存地址绑定给标识符add_staff.
def add_staff():
    print('添加员工')


# 检测代码, 申请内存地址存储函数体代码块, 并将函数体代码块的内存地址绑定给标识符del_staff.
def del_staff():
    print('删除员工')


func_dic = {'1': add_staff, '2': del_staff}

# 获取元素得到函数对象, 加上括号执行函数体代码.
func_dic['1']()  # 添加员工
func_dic['2']()  # 删除员工

2. 函数嵌套

2.1 嵌套调用
在函数中调用其它函数.
def func1():
    print('form func1')


def func2():
    func1()  # form func1
    print('form func2')


func2()  # form func2

2.2 嵌套死循环
两个函数体内互相调用函数, 默认只有1000层嵌套, 可以设置源码修改默认嵌套层数, 超出就报错:
: RecursionError: maximum recursion depth exceeded.
: 递归错误:超过了最大递归深度.
def func1():
    # 在func1中调用func2
    func2()


def func2():
    # 在func2中调用func1
    func1()


# 启动
func2()

2022-12-11_01680

2.3 函数嵌套定义
函数嵌套定义: 一个函数里用def语句来创建其它的函数的情况.
目的: 将复杂的功能全部隐藏, 暴露一个接口, 供操作者使用,
操作者不需要明白内部的代码是干什么的, 只需要知道接口的功能, 和使用方法即可.
def all_func(choices):
    def add_staff():
        print('添加员工')

    def find_only():
        print('查询员工')

    def modify_salary():
        print('修改薪资')

    def all_staff():
        print('所有信息')

    def del_staff():
        print('删除员工')

    func_dic = {
        '1': add_staff,
        '2': find_only,
        '3': modify_salary,
        '4': all_staff,
        '5': del_staff,
    }
    if choices in func_dic:
        func_dic[choices]()
    else:
        print('功能为开放!')


while True:
    print("""
    1. 添加员工
    2. 查询员工
    3. 修改薪资
    4. 所有信息
    5. 删除员工
    """)
    choice = input('请输入编号>>>:')
    all_func(choice)

3. 闭包函数

闭包函数 = 函数对象 + 函数嵌套定义 + 名称空间与作用域.
闭包函数满足以下两个条件:
* 1. 闭函数: 定义在函数内部的函数, 闭包函数是一种为函数传参的方案.
* 2. 包函数: 内部局部函数引用了外层局部函数的名称.
3.1 直接传参
def func(x, y):
    print(x, y)


func(1, 2)

3.2 闭包函数传参
# 演变过程1
def func1():
    x = 1
    y = 2

    def func2():
        # 在函数内部使用外部的函数的名称
        print(x, y)  # 1 2

    return func2


func = func1()
func()

# 演变过程2
def func1(x, y):
    def func2():
        print(x, y)  # 1 2

    return func2


func = func1(1, 2)
# 闭包函数
func()

4. 递归函数

递归函数: 在运行过程中直接或间接的调用自己, 一种基于函数实现循环.
默认嵌套1000, 超出报错, 可以设置参数修改嵌套层次, 某些电脑可能只能达到99?层.
递归是要有一定条件的:
* 1. 每次递推之后复杂度相较于上一次一定要有所下降.
* 2. 必须要能回溯.

递归分两步:
* 1. 递推: 一层层往下推导结果, 递推满足某个条件后, 开始回溯.
* 2. 回溯: 依据最后的结果回溯得到最初问题的答案.
4.1 示例1
推导第一个个小孩子的年龄:
现在有五个小朋友: 1 2 3 4 5          递推总次数 count = 5    
第一个小朋友说比第二个小朋友大 2          count - 1   + 2 
第二个小朋友说比第三个小朋友大 2          count - 1   + 2 
第三个小朋友说比第四个小朋友大 2          count - 1   + 2 
第四个小朋友说比第五个小朋友大 2          count - 1   + 2 
第五个小朋友18                          count = 0  return 18   回溯 

image-20221212054838441

def get_age(count):
    count -= 1  # 递推重做的事
    if count == 0:  # 回溯的条件
        return 18
    return get_age(count) + 2  # 回溯放回重复做的事


print(get_age(5))

# 省略写法
def get_age(count):
    if count == 1:
        return 18
    # 在这一步开始执行函数
    return get_age(count - 1) + 2  


print(get_age(5))  # 26

4.2 示例2
打印出列表中每一个整型元素.
list1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, [11, [12, [13, [14, ]]]]]]]]]]]]]]
list1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, [11, [12, [13, [14, ]]]]]]]]]]]]]]


def print_element(l1):
    # 遍历列表
    for i in l1:
        # 元素是整型则打印
        if type(i) == int:
            print(i)
        # 全是是嵌套列表, 则继续递归处理列表
        else:
            print_element(i)


print_element(list1)

4.3 示例3
算法: 解决问题的最高效方式.
使用递归二分法快速找出序列序列的某个值. 查找的元素就在数据集的头尾那么该算法不是很高效.

list1 = [11, 23, 43, 57, 68, 76, 81, 99, 123, 321, 432, 567, 666, 712, 899, 999, 1111]
将列表从中间一分为二, 判断中间的值, 比查找的值大还是小.
在每次的二分之一基础上筛选掉一半的值, 找到最后, 要么存在要么不存在.
l1 = [11, 23, 43, 57, 68, 76, 81, 99, 123, 321, 432, 567, 666, 712, 899, 999, 1111]

# 查找的值
num = 23


# 获取中间的值的索引, 整除
def find_num(list1, num):
    if not list1:
        print('没有这个数据')
        return
    
    # 获取中间值
    average = len(list1) // 2
    if list1[average] < num:
        # 获取右边的列表
        r_list = list1[average + 1:]
        find_num(r_list, num)
    elif list1[average] > num:
        # 获取右边的列表
        l_list = list1[:average]
        find_num(l_list, num)
    else:
        print('找到了')
        return


find_num(l1, num)

l1 = [11, 23, 43, 57, 68, 76, 81, 99, 123, 321, 432, 567, 666, 712, 899, 999, 1111]


def find_num(list1, num):
    # 结束条件 列表为空
    if len(list1) == 0:
        print('查找的数字不存在!')
        return

    # 定义一个中间值
    middle = len(list1) // 2  # 只能是整除 1//2 = 0

    print(middle, list1[middle], num)
    # 输入的值与中间值做比较
    if num < list1[middle]:
        # 比中间值小向 往列表的左←边查找
        list1 = list1[0:middle]
        find_num(list1, num)

    # 比中间值大向 往列表的右→边查找
    elif list1[middle] < num:
        list1 = list1[middle + 1:]
        find_num(list1, num)

    # 不大不小就找到了.
    else:
        print('找到了')
        return


input_num = int(input('输入查找的数字>>>:').strip())
find_num(l1, input_num)

5. 匿名函数

匿名函数: 指没有名字的函数, 通常只使用一次, 在需要一个函数, 但是又不想思考起名字时使用.
一般不会单独使用, 都是配合其他函数一起使用.
格式: lambda 形参: 计算返回值的表达式
返回形参处理的结果.
# 加上括号调用,  括号内传入参数
print((lambda x: x ** 2)(2))  

为匿名函数起名字, 会提示不规范:
: PEP8: E731 do not assign a lambda expression, use a def.
: PEP8: E731不指定lambda表达式,请使用def.
# 会出现PEP8提示
func = lambda x: x ** 2
res = func(2)
print(res)  # 4

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值