装饰器基础学习02

装饰器模板

def outer(被装饰的函数的函数名):
	def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的操作')
        res = func(*args, **kwargs)
        print('执行被装饰对象之后可以做的操作')
        return res
    return 内部函数名

'''针对两个地方的*args和**kwargs的作用'''
def index(*args,**kwargs):
	pass

def func(*args,**kwargs):
	index(*args,**kwargs)
'''
args = ([1,2,3,4],)
kwargs = {'name':'jason','age':18}
index(*([1,2,3,4],),**{'name':'jason','age':18})
index([1,2,3,4],name='jason',age=18)
转换来转换去其实就是给func穿什么相当于给index传什么
'''
func([1,2,3,4],name='jason',age=18)
'''
[1,2,3,4],是多余的位置参数给了args以一个数据值,以元组的形式
name='jason',age=18,是多余的关键字参数给了kwargs,以字典的形式
'''

装饰器语法糖

@outer  # 相当于这个,index = outer(index)
def index():
    pass

装饰器修复技术

from functools import  wraps
def outer(func_name):
    @wraps(func_name)  # 是为了让装饰器不容易被别人发现,做到真正的以假乱真,不会影响代码,写不写都可以
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res
    return inner

作业讲解

1.编写一个用户认证装饰器
  基本要求
   执行每个函数的时候必须先校验身份 eg: jason 123
  拔高练习(有点难度)
   执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
     函数:register login transfer withdraw 
   提示:全局变量 记录当前用户是否认证
is_login = {'is_login': False, }  # 可变类型在局部名称空间中更改的话是不需要去创建一个新的名字
# 编写装饰器
def lonin_auth(func_name):
    def inner(*args, **kwargs):
        # 判断全局字典is_login对应的值是否True
        if is_login.get('is_login'):
            # 如果是True,说明用户已经登陆过了,直接叫他执行要执行的操作
            res = func_name(*args, **kwargs)
            return res
        # 校验用户身份
        username = input('输入用户名:').strip()
        password = input('输入密码:').strip()
        if username == 'jason' and password == '123':
            res = func_name(*args, **kwargs)
            # 登录成功后,执行用户想要执行的操作
            is_login['is_login'] = True  # 用户登录过后的记录
            return res
        else:
            print('用户名权限不够')
    return inner
@lonin_auth
def register():
    print('注册功能')
@lonin_auth
def login():
    print('登录功能')
@lonin_auth
def transfer():
    print('转账功能')
@lonin_auth
def withdraw():
    print('体现功能')

register()
login()
transfer()
withdraw()

多层装饰器

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(wrapper2)
@outter2  # wrapper2 = outter2(wrapper3)
@outter3  # rapper3 = outter3(正真的index函数名)
def index():
    print('from index')
index()
'''
语法糖的功能:
    1.会自动将下面紧挨着的函数当作参数传递给@符号后面的函数名(加括号调用)
    2.如果语法糖上面还有语法糖那么他的返回值不是函数名,要看他调用函数后的返回值
    3.wrapper3 = outter3(正真的index函数名)中的wrapper3是一个函数名,然后上面又是一个语法糖,然后又触发了1的条件
    ....
    4.最后用index去接收
结果
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
'''

有参装饰器

1.在里加入参数

image-20220706205729452

2.在inner里加入参数

image-20220706211139127

3.正确思路

image-20220706212246741
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')  # 函数名加括号执行优先级   @login_auth
def index():
    print('from index')
index()
'''
举例说明
在装饰器内部可以切换多种数据来源
    1.原本装饰器按照是一步一步向下执行
    2.现在是在获取用户名之后有多种情况不再是直接的向下执行,言外之意就是在某个地方需要用到分支情况了
    3.      if condition == '列表':
                print('使用列表作为数据来源')
            elif condition == '字典':
                print('使用字典作为数据来源')
            elif condition == '文件':
                print('使用文件作为数据来源')
            else:
                print('目前只有上面几种方式')
        这个时候condition是一个不固定的值,它可以是列表、字典或者文件,而这个值是我们传进来的,那么函数里的传值方式有俩种,这个时候就要看是哪一种的传值
    4.在最外面加一层函数
总结:如果我们再写一个装饰器的时候,它的内部需要传入额外的数据来控制代码的分支,这种情况下可以在原有的基础上再套一个函数,并且可以传入多个值,不需要再套函数
'''

递归函数

1.递归函数简介

# 直接调用:自己调用自己
def index():
    print('from index')
    index()
index()
# 间接调用:俩个函数直接互相调用
def index():
    print('from index')
    func()
def func():
    print('from func')
    index()
'''
在python中自己调用自己是有次数限制的,官方给出的限制是1000 有时候可能会有些许偏差(997 998...)
'''
import sys
print(sys.getrecursionlimit())  # 1000  获取递归最大次数
sys.setrecursionlimit(2000)  # 可以修改最大次数
print(sys.getrecursionlimit())

2.递归函数的应用场景

递推:一层层往下寻找答案

回溯:根据已知的条件推导最终答案

条件:每次调用都距离答案跟近一点,一次比一次简单,最终必须要有一个结束条件

例题说明:

l1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, ]]]]]]]]]]
# 循环打印出列表中所有的数字
'''
分析过程:
1.for循环l1里面所有的数据值
2.判断当前数据值是否是数字 如果是则打印
3.如果不是则继续for循环里面所有数据值
4.判断当前数据值是否是数字 如果是则打印
5.如果不是则继续for循环里面所有数据值
6.判断当前数据值是否是数字 如果是则打印
....
由此可见,总有一段代码会重复
'''
image-20220706215509275
def get_num(l1):
    for i in l1:
        if isinstance(i,int):
            print(i)
        else:
            get_num(i)
get_num(l1)

算法之二分法

1.什么是算法?

算法是解决问题的方法,算法永远都在精进,但是很少有最完美的算法。

2.二分法

是算法里面最简单的算法,主要应用于有序序列中,原理是每次查找都将原序列折半,逐渐缩小查找范围的一种算法

每次取中值,如果高了则继续取下半部分的中值,如果低了则取上半部分的中值,以此类推,最后找到正确猜测值。

存在的缺陷:查找的数如果在开头或者结尾 那么二分法效率更低,只能查找有序序列。

例题展示:

l1 = [2, 4, 6, 8, 10, 15, 18, 23, 34, 56, 78, 90, 101, 120]
def get_num(l1, target_num):
    # 添加递归函数的结束条件
    if len(l1) == 0:
        print('找不到')
        return
    # 1.获取中间那个数,总的长度去整除2,就是中间的那个值
    middle_index = len(l1) // 2
    middle_value = l1[middle_index]
    # 2.判断取到的中间值与我们要找的目标数字谁打谁小,然后分情况讨论,利用if..elif
    if target_num > middle_value:
        # 3.如果目标函数大于中间数,说明目标函数在右边,既然大一的话那么就说明比它本身大就可以在缩小一点范围
        right_l1 = l1[middle_index + 1:]
        # 3. 同样在右边在进行同样的操作,查找中间值然后进行比较
        # 经过分析得知 应该使用递归函数
        print(right_l1)
        get_num(right_l1, target_num)
    elif target_num < middle_value:
        # 4.说明要查找的数在数据集左半边  如何截取左半边
        left_l1 = l1[:middle_index]  # 左边不需要-1,索引是顾头不顾尾的 
        # 4.同样在左边在进行同样的操作,查找中间值然后进行比较
        # 经过分析得知 应该使用递归函数
        print(left_l1)
        get_num(left_l1, target_num)
    else:
        print(target_num)
get_num(l1, 18)

作业

# 1.尝试编写有参函数将多种用户验证方式整合到其中
# 	直接获取用户数据比对
#  	数据来源于列表
#  	数据来源于文件
def date_type(date_type):
    def outer(func_name):
        def inner(*args, **kwargs):
            if date_type == '直接调用':
                print('直接使用数据来源 比对用户数据')
                res = func_name(*args, **kwargs)
                return res
            elif date_type == '列表':
                print('使用列表作为数据来源 比对用户数据')
                res = func_name(*args, **kwargs)
                return res
            elif date_type == '文件':
                print('使用文件作为数据来源 比对用户数据')
                res = func_name(*args, **kwargs)
                return res
            elif date_type == '字典':
                print('使用字典作为数据来源 比对用户数据')
                res = func_name(*args, **kwargs)
                return res
            else:
                print('权限不够  滚蛋')
        return inner

    return outer
@date_type('直接调用')
def from_user():
    pass
@date_type('列表')
def from_list():
    pass
@date_type('文件')
def from_file():
    pass
@date_type('字典')
def from_dict():
    pass
from_user()
from_list()
from_file()
from_dict()

# 2.尝试编写递归函数
# 推导指定某个人的正确年龄
# eg: A B C D E  已知E是18 求A是多少
# 分析,首先确定E的年龄,然后通过E的年龄去推其他的年龄
user_list = ['A', 'B', 'C', 'D', 'E']
index = 0
def get_age():
    global index
    if user_list[index] == 'E':
        return 18  # 返回E的年龄
    else:
        index += 1  # 索引加1
        age = get_age() + 3  # 年龄加3
        return age
age = get_age()
print(age)
posted @ 2022-07-06 22:21  张张包~  阅读(43)  评论(0编辑  收藏  举报