多层装饰器、有参装饰器、递归函数、算法介绍以及简单算法二分法
今日学习内容总结
在上周的学习中,我们已经初步使用函数,并且体会到函数的便利之处,其中装饰器尤为明显。因为装饰器可以在不改变 被装饰对象原代码以及调用方式的情况下增加新的功能。
多层装饰器
多层装饰器,多层装饰器在装饰的时候,它的顺序是由下往上,而在执行的时候,它的顺序由上到下。多层装饰器案例:
# 多层装饰器展示
def outter1(func1):
print('加载了outter1')
def wrapper1(*args, **kwargs):
print('执行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2):
print('加载了outter2')
def wrapper2(*args, **kwargs):
print('执行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3):
print('加载了outter3')
def wrapper3(*args, **kwargs):
print('执行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1
@outter2
@outter3
def index():
print('from index')
index()
# 调用过程分析
@outter1 # index = outter1(wrapper2)
@outter2 # wrapper2 = outter2(wrapper3)
@outter3 # wrapper3 = outter3(最原始的index函数地址)
def index():
print('from index')
# @outter3紧贴index(),所以此时outter3调用的是index(),而返回的实际上是wrapper3,所以wrapper3 = outter3(index).(这里传入的是最原始的index函数内存地址)
# @outter3的调用完之后,这时@outter2看到的是wrapper3,所以这时outter2调用的就是wrapper3,而返回的是wrapper2,所以有 wrapper2 = outter2(wrapper3)
# 同理,当@outter2调用完之后,@outter1看到的就是wrapper2,所以outter1这时调用的就是wrapper2,因为outter1的上层没有语法糖继续调用,所以这里就给它起名和被装饰函数同名,也就是index,所以有index = outter1(wrapper2)
# 根据上面的例子中,调用wrapper3、wrapper2、wrapper1之前会打印一句话,于是,根据我们推理的加载顺序,在没有调用index()时它打印的顺序就是
"""
加载了outter3
加载了outter2
加载了outter1
"""
# 由上述分析可以了解到多层装饰器的执行顺序。
有参装饰器
有参装饰器模板:
# 有参装饰器
def 有参装饰器(x,y,z):
def outter(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
return outter
@有参装饰器(1,y=2,z=3)
def 被装饰对象():
pass
在调用有参装饰器时,左侧是语法糖结构,右侧是函数名加括号结构。有参装饰器目的仅仅是给装饰器传递额外的参数,并且最多就三层嵌套。
递归函数
递归函数的本质,就是函数在运行过程中直接或者间接的调用了自身。所以递归函数也称为函数的递归。代码示例:
# 递归函数
def recursion():
return recursion()
# 如果一个函数在内部调用自身,这个函数就叫做递归函数,所以这是递归函数的简单定义,什么也做不了。同时这也是直接调用自己
# 间接调用自己
def index():
print('jason nb')
func()
def func():
print('jason handsome')
index()
index()
# 这种间接使用方式运行会报错:maximum recursion depth exceeded while calling a Python object 因为一旦调用,会一直循环相互调用函数。所以这是python最大递归深度。官方给出的回答是1000。
上述示例代码,都是函数的无限循环过程,但是函数的递归不应该是无限循环的过程。所以真正的递归函数应该要满足两个要求:1.每次递归 复杂度必须降低(下一次递归要比上一次递归解答),也就是越往下递归应该离解决问题的答案越近。2.必须要有明确的结束条件。代码示例:
# 真正的递归函数 计算1到100之间相加之和;通过循环和递归两种方式实现
def sum_recu(n):
if n > 0:
return n + sum_recu(n - 1)
else:
return 0
sum = sum_recu(100)
print(sum) # 5050
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。这就是递归的优点。
算法之二分法
算法
算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
一个算法应该具有以下五个重要的特征:
有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性:算法的每一步骤必须有确切的定义;
输入项:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
输出项:一个算法有一个或多个输出,以反映对输入数据加工后的结果,没有输出的算法是毫无意义的;
可行性:算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成,也称之为有效性。
算法之二分法
二分法是算法里面最入门的一个,主要是感受算法的魅力所在。
二分法使用有前提: 数据集必须有先后顺序(升序 降序)
a = [1, 123, 465, 457, 555, 666, 777, 778, 779, 805, 1000] # 查找一个数,666
def findup(a, lookup):
if len(a) == 0: # 考虑没有你找的数据的情况,列表a可能无限制的二分
print('没有该数据')
return
res = len(a) // 2 # 获取中间元素的索引值,只能是整数
if lookup > a[res]: # 判断中间索引对应的数据与目标数据的大小
find_res = a[res + 1:]
print(find_res)
findup(find_res, lookup) # 对右侧进行再次二分,重复执行相同代码,并且复杂程度降低
elif lookup < a[res]: # 判断中间索引对应的数据与目标数据的大小
look_res = a[:res]
print(look_res)
findup(look_res, lookup) # 对左侧进行再次二分,重复执行相同代码,并且复杂程度降低
else:
print('有该数据', lookup)
findup(a, 666)
这就是一个简单的算法,但是二分法也是有缺陷的。他只适合在数据量庞大的数据集中去寻找。如果如果要找的元素就在数据集的开头,二分更加复杂。同时数据集必须有顺序。并且目前没有最完美的算法,都有相应的限制条件

学习内容总结
浙公网安备 33010602011771号