装饰器的拓展与函数算法
1 > 装饰器的拓展
1.1 > 多层装饰器
多层装饰器的简而言之就是有多层的装饰器,嵌套在了一块,就比如说在装饰器的外层还有一层装饰器。那么当我们利用语法糖在遇到多层的装饰器时,具体的代码执行流程是什么样的呢,下面依此进行介绍,代码如下。
def outter1(func1):
print('加载了outer1')
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()
当我们开始调用index时 会先调用装饰器outter3,所以会先答应outter当中的
当我们开始调用index时 它的流程如图:
所以说当多层装饰器在一起时他会先将装饰器当中的外层函数传递给上一层的装饰器,在最后一层装饰里在将上一层的外层函数传递给其内层的函数,再执行内层的函数体代码。
1.2 > 有参装饰器
当我们在使用装饰器的时候,当中的数据来源可能会有很多种,比如说全局名称空间的字典啊,全局的列表、文本文件、数据库。那么我们想要使用数据是肯定需要想办法将其传入装饰器当中的局部名称空间当中的。具体操作代码如下:
def outer(source_data):
def login_auth(func_name): # 不能动 只能接收一个被装饰对象名字
def inner(*args, **kwargs): # 不能动 是专门用来给被装饰的对象传参的
username = input('username>>>:').strip()
password = input('password>>>:').strip()
if source_data == '1':
print('使用字典的方式处理数据')
elif source_data == '2':
print('使用列表的方式处理数据')
elif source_data == '3':
print('使用文件操作处理数据')
else:
print('其他操作情况')
res = func_name(*args, **kwargs)
return res
return inner
return login_auth
@outer('1')
def index():
print('登录成功')
index()
上述代码在login_auth当中找不到source_data 那么我们就去层找,没有找到我们就利用闭包函数给它传参,在闭包函数添加一个形参source_data。然后我们在使用装饰器的时候就可以在括号当中写入一个实参,这样就可以使得source_data = 这个实参,并且可以在装饰器当中进行使用。这是一种特别复杂的装饰器,几乎很少见。
2 > 递归函数
递归是一个函数过程在定义中直接或者间接调用自身的一种方法,它通常把一个大型的复杂问题层层转化为一个与原问题相似,但规模较小的问题进行求解。如果一个函数中调用了函数本身,这个函数就是递归函数。递归函数只需少量代码就可以描述出解题过程所需要的多次重复计算,大幅减少了程序的代码量。
函数递归调用时,需要确定两点,一是递归公式;二是边界条件。递归公式是递归求解过程中的归纳项,用于处理原问题以及与原问题规律相同的子问题;边界条件即终止条件,是用于终止递归。
2.1 > 直接调用自己
def index():
print('from index')
index()
index()
2.2 > 间接调用自己
def index():
print('from index')
func()
def func():
print('from func')
index()
func()
2.3 > 最大递归深度
如果这样写的话它会无限的调用自己,但是python解释器它自带应急机制,达到了最大递归深度再继续调用就会报错,结束调用。默认的最大递归深度是1000,但是在跑的时候各个计算机的性能不同,最大递归深度也会有所不同有997的,也有998的。相关代码如下:
import sys
print(sys.getrecursionlimit()) # 获取默认的最大递归深度
sys.setrecursionlimit(2000) # 还可以修改最大递归深度
2.4 > 编写方式
我们编写一个求阶乘的递归函数代码:
def factorial(num):
if num == 1:
return 1
else:
return num*factorial(num-1)
3 > 算法之二分法
3.1 > 二分法原理
获取数据集中间的元素 比对大小
如果中间的元素大于目标数据 那么保留数据集的左边一半
如果中间的元素小于目标数据 那么保留数据集的右边一半
然后针对剩下的数据集再二分
如果中间的元素大于目标数据 那么保留数据集的左边一半
如果中间的元素小于目标数据 那么保留数据集的右边一半
3.1 > 二分法实战
l1 = [13,21,35,46,52,67,76,87,99,123,213,321,432,564,612]
查找一个数 123
def get_target(l1,target_num):
# 最后需要考虑找不到的情况 l1不可能无限制二分
if len(l1) == 0:
print('不好意思 真的没有 找不到')
return
# 1.获取中间元素的索引值(只能是整数)
middle_index = len(l1) // 2
# 2.判断中间索引对应的数据与目标数据的大小
if target_num > l1[middle_index]:
# 3.保留数据集右侧
l1_left = l1[middle_index+1:]
# 3.1.对右侧继续二分 重复执行相同代码 并且复杂度降低
print(l1_left)
get_target(l1_left,target_num)
elif target_num < l1[middle_index]:
# 4.保留数据集左侧
l1_right = l1[:middle_index]
print(l1_right)
# 4.1.对右侧继续二分 重复执行相同代码 并且复杂度降低
get_target(l1_right,target_num)
else:
print('找到了',target_num)
get_target(l23)
二分法也是有许多的缺陷,1,如果要找的元素就在数据集的开头 二分更加复杂。2,数据集必须有顺序,目前没有最完美的算法 都有相应的限制条件