函数篇:多层及有参装饰器、递归函数、算法之二分法

2022.3.21学习笔记

qnoJo9.jpg

  • 多层语法糖
  • 有参装饰器
  • 递归函数
  • 算法之二分法

一、多层语法糖(装饰器)

首先,我们需要先定义多个装饰器
def outer1 (func1):
    print('执行了outer1')
    def inner1(*args, **kwargs):
        print('执行了inner1')
        res1 = func1(*args, **kwargs)
        return res1
    return inner1
        
 def outer2 (func2):
    print('执行了outer2')
    def inner2(*args, **kwargs):
        print('执行了inner2')
        res2 = func2(*args, **kwargs)
        return res2
    return inner2

def outer3 (func3):
    print('执行了outer3')
    def inner3(*args, **kwargs):
        print('执行了inner3')
        res3 = func3(*args, **kwargs)
        return res3
    return inner3

# 再定义一个函数
@outer1  # inner2 = outer1(inner2)
@outer2  # inner3 = outer2(inner3)
@outer3  # index = outer3(index) 
def index():
    print('执行了index')
观察这个多层装饰器,结果会如何运行呢?
其实,上面装饰器的外层函数是自下而上进行调用的,而到了最上层装饰器之后,下面调用index时,再自上而下运行内部函数

二、有参装饰器

首先有参装饰器顾名思义就是有参数的装饰器,如:

@outer(a)

思考:这个a参数如何传入装饰器内部使用呢?

不妨将outer装饰器写出来,如下:

def outer (func):
    def inner1(*args, **kwargs):
        print(A)  # 假设装饰器内需要a参数
        res = func(*args, **kwargs)
        return res
    return inner

可以发现,闭包函数内外两层函数的参数已经写死了,不能再添加参数了,那么我们如何从外部传入参数呢,只有再包一层函数了:

def data(A):    
    def outer (func):
        def inner1(*args, **kwargs):
            print(A) 
            res = func(*args, **kwargs)
            return res
        return inner
    return outer

@data(a)  # 先不看@符号,data(a)的返回值为outer,因此@data(a) == @outer,即index = outer(index)
def index():
    ...

这样的话采用函数多层嵌套的方式成功传入参数,也没有改变装饰器的本质,大功告成!

这里需要注意一个知识,函数执行的优先级是最高的,因此需要先看@符号右边的结果是什么

三、递归函数

特征:在函数运行过程中直接或者间接调用了自身的函数称之为递归函数,ru:

1.直接调用
def index():
    print('666')
    index()
2.间接调用
def index():
    print('111')
    func()
def func():
    print('222')
    index()
# 结果报错,超出最大递归深度

这样调用的结果就是函数无限执行下去直到达到解释器的限制,最大递归限制

最大递归限制(深度):官网给出的是1000,回答997,998等上下都可以,而且最大递归限制可以更改,如下:

import sys
print(sys.getrecursionlimit())  # 获取默认的最大递归深度1000
sys.setrecursionlimit(2000)  # 还可以修改最大递归深度

那么我们仍然会发现,这样的函数运行是毫无意义的,因为永远没有结果,因此我们需要加上一些人为的条件,让函数运行达到一定条件后,结束运行,并得到一个结果。

需求:

如果教室里面前面一个学生的年龄比后面一个学生年龄大两岁,最后一排的学生年龄为18岁,那么,第一排年龄是多少?

# 定义一个函数进行计算
def get_age(num):
    if num == 1:
        return 18  # 结束条件
    return ger_age(num-1) + 2  # 计算公式,后面比前面小2岁,因此需要+2
print(get_age(5))
# get_age(5) = get_age(4) + 2 
# get_age(4) = get_age(3) + 2 
# get_age(3) = get_age(2) + 2 
# det_age(2) = get_age(1) + 2 
像这样一直倒推到num为1的时候,我们知道get_age = 18,再把之前的结果都相加,就能得到结果。
像这样在最初的函数输入有几排学生就能得到第一排学生的年龄
    

这就是递归函数,可以发现他的特征有:

1、每次递归,复杂度都会降低,相当于每次递归都与答案越来越近

2、必须要有明确的结束条件

拓展:

isinstance(i, int)  # 判断i是否为右边整型

四、算法之二分法

二分法是算法里面最入门的一个,主要为了感受算法的魅力

使用前提:数据集必须有升序或者降序

l = [13,14,35,46,58,67,77,86,92,123,245,365,421,578,654,768]
# 问题:查找一个数123
常规操作:for循环列表
优点:无需注重顺序
缺点:效率不高

那么如果使用二分法呢,先定义一个函数:

def get_num(l1,target_num):  # 定义函数,先不写内容
    ...
get_num(l, 123)  # 调用函数

大概形式是这样,然后需要思考,怎么拿到最中间的数,然后计算呢

def get_num(l1,target_num): 
    if len(l1):  # 即列表l1长度不为0时
        mid_num = len(l1) // 2  # 列表长度整除2
        if l1[mid_num] > target_num:  # 判断目标数字在中间数的左边或者右边
            l1_left = l1[:mid_num]
            print(l1_left)
            get_num(l1_left, target_num)  # 得到一半列表,继续递归
        elif l1[mid_num] < target_num:
            l1_right = l1[mid_num:]
            print(l1_right)
            get_num(l1_right, target_num)
        else:
            print('找到了', target_num)
get_num(l, 123) 

这样,利用二分法就找到了123这个数字,但是二分法也有缺陷

优点:效率高,不需要一个一个找
缺点:如果需要的数据在头尾,没有for循环简单

后面还需要了解一些算法,比如快排、插入、冒泡、堆排等

bhbSqH.jpg

posted @ 2022-03-21 17:11  马氵寿  阅读(106)  评论(0)    收藏  举报