多层装饰器

  • 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
  • 多个语法糖装饰一个函数名:从下往上执行最后一个语法糖才会做重命名操作
def outter1(func1): # func1 = wrapper2函数名
    print('加载了outter1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2): # func2 = wrapper3函数名
    print('加载了outter2')
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3 = 真正的index函数名
    print('加载了outter3')
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

# 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
@outter1 # index = outter1(wapper2)   index是wrapper1
@outter2 # wrapper2 = outter2(wrapper3)
@outter3 # wrapper3 = outter3(真正的index函数名)
def index():
    print('from index')

index()
# 加载了outter3
# 加载了outter2
# 加载了outter1
# 执行了wrapper1
# 执行了wrapper2
# 执行了wrapper3
# from index

有参装饰器

  • 有参装饰器:编写一个装饰器时,当这个装饰器内部需要外界传入额外的数据来控制代码的分支,就需要给原来的装饰器模板再套用一层,加语法糖时就直接添加额外的数据
    • 目的:为了给装饰器代码传递更多额外的数据
def outer(condition,type_user): # 给装饰器传参数(有几个参数就传几个参数)
    def login_auth(func_name):  # 这里不能再填写其他形参
        def inner(*args, **kwargs):  # 这里不能再填写非被装饰对象所需的参数
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            # 应该根据用户的需求执行不同的代码
            if type_user =='jason':print('VIP')
            if condition == '列表':
                print('使用列表作为数据来源 比对用户数据')
            elif condition == '字典':
                print('使用字典作为数据来源 比对用户数据')
            elif condition == '文件':
                print('使用文件作为数据来源 比对用户数据')
            else:
                print('目前只有上面几种方式')
        return inner
    return login_auth
@outer('文件','jason') # 函数名加括号 优先级最高
def index():
    print('from index')
index()

递归函数

概念

  • 概念:在函数内部调用自己的函数被称之为递归
# 递归调用:直接调用
def index():
	print('from index')
	index()
index()

# 递归调用:间接调用
def index():
	print('from index')
	func()

def func():
	print('from func')
	index()

func()
  • python中允许函数最大递归调用次数
    • 官方给出的限制是1000,代码验证可能会有偏差(996 997 998...都有可能)
count = 0
def index():
	print('from index')
	global count
	count += 1 # count = count + 1
	print(count)
	index()
index()


import sys
print(sys.getrecursionlimit()) # 1000 获取递归最大次数

sys.setrecursionlimit(2000) # 自定义最大次数
print(sys.getrecursionlimit())

函数递归应用场景

  • 递推:层层往下寻找答案(每一次都是基于上一次进行下一次的执行)

  • 回溯:遇到终止条件,则从最后往回返一级一级的把值返回来

  • 递归特点

    1. 必须要有一个明确的结束条件
    2. 每次进入更深一层递归时,问题规模(计算量)相比上次递归都应有所减少
    3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
l1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10,]]]]]]]]]]
# 1.for循环l1里面所有的数据值
# 2.判断当前数据值是否是数字 如果是则打印
# 3.如果不是则继续for循环里面所有数据值
# 4.判断当前数据值是否是数字 如果是则打印
# 5.如果不是则继续for循环里面所有数据值
# 6.判断当前数据值是否是数字 如果是则打印
# ...
def get_num(l1):
	for i in l1: # for循环自带结束条件,不用手动加结束条件
		if isinstance(i, int): # 判断i绑定的数据值是不是整型
			print(i)
		else:
			get_num(i)
get_num(l1)

递归的优缺点

  • 递归的优点:
    1. 递归函数让代码看着更干净整洁
    2. 使用递归可以将复杂的任务分解成更简单的子问题
    3. 和使用嵌套相比,使用递归更容易生成序列
  • 递归的缺点:
    1. 有时递归背后的逻辑很难遵循
    2. 占用大量内存和时间,递归调用效率低
    3. 递归函数很难调试

算法之二分法

  • 算法:解决问题的方法
  • 算法永远都在精进,但是很少有最完美的算法
  • 二分法:所有算法里最简单的算法
  • 二分法使用前提:数据集必须有顺序(升序/降序)
  • 二分法原理:
    • 获取数据集中间的元素 比对大小
      • 如果中间的元素大于目标数据,那么保留数据集的左边一半
      • 如果中间的元素小于目标数据,那么保留数据集的右边一半
    • 数据 那么保留数据集的右边一半
      • 如果中间的元素大于目标数据,那么保留数据集的左边一半
      • 如果中间的元素小于目标数据,那么保留数据集的右边一半
l1 = [11, 23, 32, 45, 65, 78, 90, 123, 432, 467, 567, 687, 765, 876, 999, 1131, 1232]


def get_num(l1, target_num):
    # 添加递归函数的结束条件
    if len(l1) == 0:
        print('不好意思 找不到')
        return
    # 1.先获取数据集中间那个数
    middle_index = len(l1) // 2
    middle_value = l1[middle_index]
    # 2.判断中间的数据值与目标数据值孰大孰小
    if target_num > middle_value:
        # 3.说明要查找的数在数据集右半边  如何截取右半边
        right_l1 = l1[middle_index + 1:]
        # 3.1.获取右半边中间那个数
        # 3.2.与目标数据值对比
        # 3.3.根据大小切割数据集
        # 经过分析得知 应该使用递归函数
        print(right_l1)
        get_num(right_l1, target_num)
    elif target_num < middle_value:
        # 4.说明要查找的数在数据集左半边  如何截取左半边
        left_l1 = l1[:middle_index]
        # 4.1.获取左半边中间那个数
        # 4.2.与目标数据值对比
        # 4.3.根据大小切割数据集
        # 经过分析得知 应该使用递归函数
        print(left_l1)
        get_num(left_l1, target_num)
    else:
        print('找到了', target_num)


# get_num(l1, 999)
get_num(l1, 1000)

  • 二分法缺陷:
    1. 数据集必须是有序的
    2. 查找的数如果在开头或结尾而分发效率更低

作业

# 1.尝试编写有参函数将多种用户验证方式整合到其中
# 	直接获取用户数据比对
#  	数据来源于列表
#  	数据来源于文件
def outer(data):
    def login_auth(func_name):
        def inner(*args, **kwargs):
            if data == '直接获取':
                username = input('username>>>:').strip()
                password = input('password>>>:').strip()
                if username == 'jason' and password == '123':
                    res = func_name(*args, **kwargs)
                    print('用户登录成功')
                    return res
                else:
                    print('用户名或密码错误,验证失败')
            elif data == '列表':
                print("用户登录成功")
                res = func_name(*args, **kwargs)
                return res
            elif data == '文件':
                print('用户登录成功')
                res = func_name(*args, **kwargs)
                return res
            else:
                print('没有这个验证方式')

        return inner
    return login_auth

@outer('直接获取')
def user_data():
    pass

@outer('列表')
def user_list():
    pass

@outer('文件')
def user_file():
    pass

user_data()
user_list()
user_file()

# 2.尝试编写递归函数
# 	推导指定某个人的正确年龄
#     	eg: A B C D E  已知E是18 求A是多少
def age(n):
    if n == 5:
        return 18
    else:
        res = age(n+1) + 2
        return res
res = age(1)
print(res)

冒泡排序

  • 冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
def bubbleSort(arr):
    n = len(arr)

    # 遍历所有数组元素
    for i in range(n):

        # Last i elements are already in place
        for j in range(0, n-i-1):

            if arr[j] > arr[j+1] :
                arr[j], arr[j+1] = arr[j+1], arr[j]

arr = [64, 34, 25, 12, 22, 11, 90]
bubbleSort(arr)

print ("排序后的数组:")
for i in range(len(arr)):
    print ("%d" %arr[i])
# 结果:
# 排序后的数组:
# 11
# 12
# 22
# 25
# 34
# 64
# 90

插入排序

  • 插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
def insertionSort(arr):

    for i in range(1, len(arr)):
        key = arr[i]
        j = i-1
        while j >=0 and key < arr[j] :
                arr[j+1] = arr[j]
                j -= 1
        arr[j+1] = key

arr = [12, 11, 13, 5, 6]
insertionSort(arr)
print ("排序后的数组:")
for i in range(len(arr)):
    print ("%d" %arr[i])

# 结果
# 排序后的数组:
# 5
# 6
# 11
# 12
# 13

快速排序

简介

  • 快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来

  • 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

  • 快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

  • 快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:

    • 快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

算法步骤

  1. 从数列中挑出一个元素,称为 "基准"(pivot)
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
def quickSort(arr, left=None, right=None):
    left = 0 if not isinstance(left,(int, float)) else left
    right = len(arr)-1 if not isinstance(right,(int, float)) else right
    if left < right:
        partitionIndex = partition(arr, left, right)
        quickSort(arr, left, partitionIndex-1)
        quickSort(arr, partitionIndex+1, right)
    return arr

def partition(arr, left, right):
    pivot = left
    index = pivot+1
    i = index
    while  i <= right:
        if arr[i] < arr[pivot]:
            swap(arr, i, index)
            index+=1
        i+=1
    swap(arr,pivot,index-1)
    return index-1

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]
 posted on 2022-07-06 21:37  念白SAMA  阅读(54)  评论(0)    收藏  举报