编程语言
首页 > 编程语言> > 三层装饰器,有参装饰器,递归,算法之二分法

三层装饰器,有参装饰器,递归,算法之二分法

作者:互联网

今日内容概要

img

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

"""
多个装饰器的加载顺序是自下而上
执行顺序是自上而下
语法糖会将紧挨着的被装饰对象的名字当作参数自动传入装饰器函数中
"""
# eg:
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()
# 输出结果:
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
"""
多个装饰器,运行顺序是从距离被装饰函数最近的一个装饰器开始的,即从wrapper1,wrapper2,wrapper3,一层一层嵌套,但从打印的结果可知,在被装饰函数之前运行的是从外到内的顺序,之后运行的是从内到外的顺序
"""

代码运行大致流程图:

466047cc1bf1195a38f9422ee47e08f

有参装饰器

# 由于语法糖@的限制,outter函数只能有一个参数,并且该函数只用来接收被装饰对象的内存地址(也就是两层不够用,需要再来一层进行传参)
from functools import wraps
def outter(source):  # 用来接收数据的来源方式,
    def login(func_name):
        @wraps(func_name)
        def inner(*args,**kwargs):
            # 校验用户数据,数据的来源可以有很多,比如全局字典,全局列表,文本文件,数据库
            # 数据的来源方式不同,处理方式就不同,对应的代码编写就不一样
            # 分支结构处理 然后根据不同的参数提示,匹配不同的流程
            if source == '1':
                print('数据来自字典')
            elif source == '2':
                print('数据来自数据库')
            elif source == '3':
                print('数据来自文件')
            res = func_name(*args,**kwargs)
            return res

        return inner
    return login
@outter('2')
def index():
    print('你是猪吗')
index()
"""
@outter('2') 左侧是语法糖结构,右侧是函数名加括号结构
先执行和函数调用outer('2')返回值是login
再执行语法糖结构@login
有参装饰器目前仅仅是给装饰器传递额外的参数,而且装饰器最多就是三层嵌套,并使用频率不高
"""

img

递归函数

# 本质:递归函数也称为函数的递归,函数在运行过程中直接或简接的调用了自身
⬇⬇		⬇⬇		⬇⬇		⬇⬇		⬇⬇		
# 函数的递归调用指的是在调用一个函数的过程中又直接或间接地调用该函数本身
"""
递归分为两个阶段:
(1):回溯:就是一次次重复的过程,这个重复的过程必须建立在每一次重复问题的复杂度都应该下降直到有一个最终的结束条件。
(2):递推:一次次往回推导的过程。
"""
# eg:几个不正确的案例
# 直接调用自己
def func1():
    print('from func1')
    func1()  
func1()
# 此代码运行后会有一个报错:maximum recursion depth exceeded while calling a Python object
这是因为此段代码相当于一个死循环,一直在调用自己,不停的申请内存空间,但好在python解释器有自带的应急机制,但有些编程语言没有这个机制,代码会执行到计算机崩溃
递归深度官方默认的是1000,但平时运行时基本上都在997,998左右
# 间接调用自己
def index():
	print('from index')
    func()
def func():
	print('from func')
    index()
func()

此代码同样会报刚才的错误
可以使用sys.getrecursionlimit()去查看递归深度,默认值为1000
虽然可以使用sys.setrecursionlimit()去设定该值
"""

函数的递归不应该是无限死循环,真正的递归函数应该要满足两个要求
1.每次递归,复杂度必须降低(下一次递归要比上一次的递归简单)
	越往下递归应该离解决问题的答案越近
2.必须要有明确的结束条件
"""
# 再来几个正确的例子
需求:我想知道我们班坐在第一排的某个学生年龄
    我问他多大了 他调皮不告诉我 说比后面那个同学大两岁
    后面的说比他后面的大两岁
    ...
    问到最后你一排 终于开口说 18岁
    ...
    知道最后一排的年级回推就可以知道第一排的年级
名词:
    递推:一层层往下问
    回溯:根据结果推结论
# 如何编写代码完成
    # 目标人物的年龄 = 后一排年龄 + 2
    # 后一排年龄 = 后后一排年龄 + 2
    # 后后一排年龄 = 后后后一排年龄 + 2
    # 后后后后一排年龄 = 18
    # 封装函数
def get_age(n):
    if n == 1:
        return 18  # 有明确的结束条件
    return get_age(n-1) + 2
print(get_age(4))
##################################################
l1 = [1,[2,[3,[4,[5,[6,[7,[8,[9,]]]]]]]]]
'''需求:循环打印出列表中每一个数字'''
# 写代码之前一定要先理清思路
"""
完整步骤 
    1.for循环大列表
    2.判断元素是否是数字 如果是则打印
    3.如果不是则for循环
    4.判断元素是否是数字 如果是则打印
    5.如果不是则for循环
    6.判断元素是否是数字 如果是则打印
    7.如果不是则for循环
ps:重复做一些事情 但是每次都比上一次简单一些 >>>:  递归函数
"""
l1 = [1,[2,[3,[4,[5,[6,[7,[8,[9,]]]]]]]]]
def func1(l):
    for i in l:
        if isinstance(i,int):  # 判断某个数据是否属于某个类型
            print(i)  # 在就打印
        else:
            func1(i)  # 不在就再调用循环
func1(l1)

算法之二分法

# 什么是算法?
就是找到问题的最优解法
实质上就是不断地将有序数据集对半分割,并检查每个分区的中间元素
二分查找操作的数据集必须是一个有序的数据集,可以是升序或降序,开始时,先找到数据集中间的元素,如果此元素要比查找的元素大,就接着在小的那一半查找,在查找时,再将那个小的数据集再一分为2进行比对,在每个更小的数据集中重复这个查找过程,直到找到查找的元素或者数据集不能再分割
# eg:
l1 = [1, 11, 24, 35, 44, 55, 66, 77, 88, 99, 142, 267, 365, 489, 520, 670, 781, 880, 999]


def func1(l1, target_num):
    if len(l1) == 0:  # 判断l1列表里还有没有数据
        print('真的没了,大哥')
        return  # 返回空值结束
    middle = len(l1) // 2  # 将l1列表一分为2
    if target_num > l1[middle]:  # 判断中间索引对应的数据与目标值的大小
        l1_left = l1[middle:]  # 保留数据集右侧
        print(l1_left)
        func1(l1_left, target_num)  # 对右侧继续二分
    elif target_num < l1[middle]:  # 判断中间索引对应的数据与目标值的大小
        l1_right = l1[:middle]  # 保留左边数据集
        print(l1_right)
        func1(l1_right, target_num)  # 继续二分
    else:
        print('找到了', target_num)  


func1(l1, 520)
二分法的缺馅:
    1.如果要找的数据就在开头,那么二分也是从中间开始切割比较,这样反而变得更加复杂
    2.这个数据集必须得是个有顺序的,没顺序还不行

img

标签:func1,return,递归,二分法,l1,print,装饰,def
来源: https://www.cnblogs.com/zhengkaijian/p/16035892.html