函数进阶:多层语法糖、有参装饰器、装饰器模板、装饰器修复技术、递归函数

一、多层语法糖

多层语法糖实际应用中出现较少,但是我们也需要了解相关的运行原理:

1、先判断距离被语法糖作用的函数最近的语法糖,它的作用就是把被装饰函数放入装饰器的函数中当参数。这个时候装饰器外层的函数会返回内部函数名。

2、接下来我们判断第二个装饰器的语法糖。这个语法糖的作用也是把下方得到的函数传到第二个装饰器中去,由于最下方那个语法糖的作用我们得到了最下方那个语法糖对应的内层函数名,所以这里就是把这个内层的函数名传到第二个装饰器中,然后我们会得到第二个装饰器返回给我们它的内层函数名。

3、最上方的语法糖的原理跟第二个类似,把第二个装饰器返回的内层函数名当成参数传到最上方语法糖对应的装饰器中,这里我们会得到最上方语法糖对应的装饰器内层的函数名称,并将她和被装饰函数的名称绑定。

'''注意语法糖会将下面紧挨着的函数当作参数传递给@符号后面的函数名运行'''


def outter1(func1):     # 12.这个时候func1就接到了wrapper2的函数名了
    print('加载了outter1')     # 13.打印加载outter1
    def wrapper1(*args, **kwargs):      # 14.在创建新的wapper1 return
        print('执行了wrapper1')        # 16.打印wrapper1
        res1 = func1(*args, **kwargs)       # 17.遇到括号优先执行 这个时候的fun1是wrapper2所以直接跳到wrapper2
        return res1
    return wrapper1     # 15. 返回 wrapper1


def outter2(func2):     # 07.所以这个时候的func2就是wrapper3
    print('加载了outter2')     # 08.打印加载outter2 
    def wrapper2(*args, **kwargs):      # 09.然后定义了wrapper2 下一步return
        print('执行了wrapper2')        # 18.打印wrapper2
        res2 = func2(*args, **kwargs)       # 19.func2就是wrapper1
        return res2
    return wrapper2     # 10.这个时候返回wrapper2就是index返回了


def outter3(func3):     # 02.这个时候fun3是真的index 运行fun3函数
    print('加载了outter3')     # 03.打印加载了outter3 函数定义不用看代码函数体代码 所以下一步 return
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')        # 20.打印wrapper3
        res3 = func3(*args, **kwargs)       # 21.func3就是index跳到index
        return res3
    return wrapper3     # 04.所以这时候index的返回值就是wrapper3 这个时候wrapper就成了函数名 再次触发 outter2


@outter1        #11.这个时候index就到了outter1在运行outter1		15.这个时候就变成伪装的 index = routter1(wrapper2)
@outter2        # 06.所以这个时候wrapper3会传到outter2里面在运行
@outter3        # 01.这个时候就会把真正的index传给outter3 所以 outer3(index) 括号优先级执行	05.所以这个时候就是 wrapper3 = outter3(index)
def index():
    print('from index')     # 22.结束


index()
    
"""
多层语法糖 加载顺序由下往上
每次执行之后如果上面还有语法糖 则直接将返回值函数名传给上面的语法糖
如果上面没有语法糖了 则变形 index = outter1(wrapper2)
"""

二、有参装饰器

当我们使用装饰器的时候,如果装饰器内部的代码需要参数的时候我们发现不能用传参的方式来添加参数了,所以我们又使用闭包函数在外层包了一个函数,打到传参的目的。

注:当我们看到语法糖后面跟着括号和参数的时候,需要先看函数名和括号内的内容,然后再应用语法糖功能,在我们运行之后会发现有参装饰器又变回了之前的普通装饰器只是多了一个局部名称空间读取变量。

# 校验用户是否登录装饰器
def outer(mode):
    def login_auth(func_name):		
        # 这里我们可以看出来是用来传函数名的,不适合加参数了
        def inner(*args, **kwargs):		
            # 这里我们又可以看出来所有的参数都是加到内部被装饰函数中的,也不适合传别的参数所以就使用了闭包函数传参
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            if mode == '1':
                print('数据直接写死')
            elif mode == '2':
                print('数据来源于文本文件')
            elif mode == '3':
                print('数据来源于字典')
            elif mode == '4':
                print('数据来源于MySQL')
        return inner
    return login_auth
'''当装饰器中需要额外的参数时>>>:有参装饰器'''

"""
函数名加括号执行优先级最高 有参装饰器的情况 
    先看函数名加括号的执行
    然后再是语法糖的操作
"""
@outer('1')
def index():
    print('from index')
index()

@outer('2')
def func():
    print('from func')
func()
# 如果我们不想使用装饰器功能了,就把语法糖部分的代码注释掉就可以正常运行了。

三、有参装饰器模板

def outter(add_n):
    def middle(func):
        def inner(*args, **kwargs):
            # 这里写一些代码需要参数的那种,把最外层的add_n用进来就可以了
            res = func(*args, **kwargs)
            return res

        return inner

    return middle


@outter('参数')
def index():
    pass

四、装饰器修复技术

装饰器修复技术不算重点知识,但是在面试的时候可以装逼。

这里需要先介绍一下help方法的作用:用于查看函数或模块用途的详细说明。

如果一些小白使用help方法查看我们被装饰函数的用法,会发现查看到的是装饰器函数的内容,这时候我们使用一些小手段就可以让help方法指向的内容回到原函数:

1、在装饰器函数上面加上from functools import wraps

2、并且在内层函数前加上@wraps(func_name),这里括号内写装饰器外层的参数名称就可以了。

def index():
	"""index函数 非常的牛"""
    pass
help(index)
help(len)
# 这是使用修复技术前的代码,返回的结果会是外层装饰器的内容

from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器的效果更加逼真 平时可以不写
    def inner(*args, **kwargs):
        """我是inner 我擅长让人蒙蔽"""
        res = func_name(*args, **kwargs)
        return res
    return inner

@outer
def func():
    """我是真正的func 我很强大 我很牛 我很聪明"""
    pass


# help(func)
# print(func)
func()
# 这个时候我们发现代码运行后返回的是被装饰函数原本的内容

五、递归函数

递归函数就是直接或间接调用函数自身的函数,当我们使用这种函数的时候,并不会出现预料之中的死循环,当循环次数达到1000左右就会被解释器强行停止,虽然官方说法是1000次,但是当我们使用时,通常会在996次左右停止。但是这种情况下的函数并不算递归函数,递归函数有以下两个条件:

1.直接或者间接调用自己
2.每次调用都必须比上一次简单 并且需要有一个明确的结束条件
递推:一层层往下
回溯:基于明确的结果一层层往上

1.函数的递归调用
	# 这里是直接和间接的调用情况
 	# 直接调用
    # def index():
    #     print('from index')
    #     index()
    # index()
    # 间接
    # def index():
    #     print('from index')
    #     func()
    #
    # def func():
    #     print('from func')
    #     index()
    #
    # func()
    '''最大递归深度:python解释器添加的安全措施'''
    # count = 0
    # def index():
    #     global count
    #     count += 1
    #     print(count)
    #     index()
    # index()
    '''官网提供的最大递归深度为1000 我们在测试的时候可能会出现996 997 998'''
    
2.递归函数
	1.直接或者间接调用自己
 	2.每次调用都必须比上一次简单 并且需要有一个明确的结束条件
		递推:一层层往下
  		回溯:基于明确的结果一层层往上
 	 """
    get_age(5) = get_age(4) + 2
    get_age(4) = get_age(3) + 2
    get_age(3) = get_age(2) + 2
    get_age(2) = get_age(1) + 2
    get_age(1) = 18
    """
    def get_age(n):
        if n == 1:
            return 18
        return get_age(n-1) + 2
    res = get_age(5)
    print(res)

当然这个重复引用的次数也不是只能是1000次,可以使用setrecursionlimit()方法改变次数

import sys 倒入系统板块
print(sys.getrecursionlimit()) 内置方法. 1000是递归最大次数
sys.setrecursionlimit(2000) 可以自定义最大次数
print(sys.getrecursionlimit())

作业

#1.利用有参装饰器编写多种用户登录校验策略
# 1.利用有参装饰器编写多种用户登录校验策略
'''
1.直接写死的 jason 123
2. 数据来源于列表 ['jason|123', 'kevin|123','tony|222']
3.数据来源于文件 jason|123\n tom|123\n
'''
def login_auth(condition):
    def outer(func_name):
        def inner(*args, **kwargs):
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            if condition == 'absolute':  # 函数体代码需要一个可变的东西,需要外界传给他
                if username == 'jason' and password == '123':
                    res = func_name(*args, **kwargs)
                    return res
                else:
                    print('用户名或密码错误')
            elif condition == 'list_type':
                  user_list = ['jason|123', 'tony|321', 'kevin|222']
                  user_data = f'{username}|{password}'
                  if user_data in user_list:
                      res = func_name(*args, **kwargs)
                      return res
                  else:
                      print('用户名或密码错误')
            elif condition == 'file_type':
                with open(r'userinfo.txt', 'r', encoding='utf8') as f:
                    for line in f:
                        real_name, real_pwd = line.split('|')
                        if real_name == username and real_pwd.strip('\n') == password:
                            res = func_name(*args, **kwargs)
                            return res
                        return inner
                    return outer

            @login_auth('absolute')
            def index(*args, **kwargs):
                print('from index')

            @login_auth('list_type')
            def func(*args, **kwargs):
                print('from func')

            @login_auth('file_type')
            def foo(*args, **kwargs):
                print('from foo')


            index()
            func()
            foo()
# 2.利用递归函数依次打印列表中每一个数据值
# 	l1 = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]
# 2.利用递归函数依次打印列表中每一个数据值
l1 = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]
"""
1.for循环l1里面的数据值
2.如果是数字 则打印
3.如果是列表 则循环
4.for循环小列表里面的数据值
5.如果是数字 则打印
6.如果是列表 则循环
7.for循环小小列表里面的数据值
8.如果是数字 则打印
9.如果是列表 则循环
"""
print(isinstance(123, int)) # 判断第一个数据值是不是属于第二个参数指定的数据类型
print(isinstance(123, str)) # 判断第一个数据值是不是属于第二个参数指定的数据类型



def get_num(l1):
    for i in l1:    # 递归函数需要结束条件 这里巧在for循环接收到空的能够被for循环的数据时 自动不执行
        if isinstance(i,int):
            print(i)
        else:
            return get_num(i)
get_num(l1)
posted @ 2022-10-12 20:39  wwwxxx123  阅读(40)  评论(0编辑  收藏  举报