装饰器

1.global与nonlocal

"""
当我们在全局定义一个变量,在函数内可以拿到,但是无法修改(要通过global)。我们在函数内定义一个变量,在函数外无法使用
"""
1.global:局部名称空间修改全局名称空间的数据

n = 100
def index():
    global n
    n = 999

index()
print(n)  # 999 

2.nonlocal:内层局部名称空间修改外层局部名称空间中的数据
    
def index():
    n = 100
    def func():
        nonlocal n
        n = 500
    func()
    print(n)
index()   

2.函数名的多种用法

函数名其实绑定的也是一块内存地址,只不过该地址里面存放的不是数据值而是一段代码,函数名加括号就会找到该代码并运行。

1.函数名可以当作变量名赋值
def index():
    print('from index')
res = index  # 将函数名index绑定的代码赋值给res
res()  # from index  # res()就相当于index()

2.可以当做函数的参数
def index():
    print('from index')
def func(a):
    print(a)
    a()
func(index)  # <function index at 0x00000128C6607B80> from index

“打印函数的函数名(不加括号)是函数的内存地址”

3.可以当做函数的返回值
def index():
    print('from index')
def func():  # 定义func()函数
    print('from func')  # 打印from func()
    return index  # 返回index函数名,并赋值给res
res = func()  # 此时 res就等于index
print(res)  # 相当于print(index),是index函数的内存地址
res()  # 调用index()函数
# from func <function index at 0x000001EC90A17B80> from index

4. 可以当做容器类型的数据
def register():
    print('注册功能')
def login():
    print('登录功能')
def withdraw():
    print('提现功能')
def transfer():
    print('转账功能')
def shopping():
    print('购物功能')
func_dict ={
    '1':register,
    '2':login,
    '3':withdraw,
    '4':transfer,
    '5':shopping
}
while True:
    choice = input('请输入您的任务编号>>>:').strip()
    if choice in func_dict:
        func_name = func_dict.get(choice)
        func_name()
    else:
        print('请输入正确任务编号')
"""
函数名(不加括号)可以放在字典的值中,调用时加括号
"""

3.闭包函数

1.定义在函数内部的函数,并且用到了外部函数名称空间的名字
"""
注意:
1.定义在函数内的函数
2.用到了外部函数名称空间中的名字
"""
def index():
    name = 'max'
    def func():
        print(f'{name}先生你好')
    func()
index()  # max先生你好
"""
由此可以得出结论:给函数传参的方式除了实参赋值,还有闭包函数。
"""
方式1:实参赋值
def index(name, age):
    print(f'{name}先生今年{age}岁了')
index('max', 25)  # max先生今年25岁了

方式2:闭包函数
def outer(name, age):
    def register():
        print(f"""
        姓名:{name}
        年龄:{age}
        """)
    return register
res = outer('jason', 18)
res()

res()  # 每执行一遍res都会打印一遍jason的信息

res = outer('max', 25)
res()  # 如果想重新传值则重新打印这两行代码

4.装饰器简介

1.概念:在不改变被装饰对象原代码和调用方式的情况下给被装饰对象添加新的功能。
    
2.本质:并不是一门新的技术,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果。
    
3.口诀:对修改封闭,对扩展开放。
    
4.储备知识:事件相关操作
	4.1时间戳:从1070年1月1日00:00到代码执行所经历的时间。
import time
print(time.time())  # 1665473850.1894429

	4.2等待若干秒之后在执行代码:
import time

time.sleep(3)
print('hello world')

	4.3统计函数执行的时间
import time
count = 1
start_time = time.time()
while count < 100:
    print('hello world')
    count += 1
end_time = time.time()
print('函数执行的时间:', end_time - start_time)  # 函数执行的时间: 0.001004934310913086

5.装饰器的推导流程

1.直接在函数调用的前后添加代码:
import time

def index():
    time.sleep(3)
    print('from index')

def home():
    time.sleep(1)
    print('from home')

start_time = time.time()
index()
end_time = time.time()
print('函数执行的时间:', end_time - start_time)  # from index 函数执行的时间: 3.0092296600341797

2.index调用的地方较多,代码不可能反复拷贝,相同的代码在不同的地方反复执行,使用函数更为便捷,定义一个get_time()函数
def get_time():
    start_time = time.time()
    index()
    end_time = time.time()
    print('函数执行的时间:', end_time - start_time)
get_time()  # from index 函数执行的时间: 3.007826566696167

3.如果这么写那么函数只能统计index()函数的运行时间,如何才能做到统计更多函数运行时间?直接传参
import time

def index():
    time.sleep(3)
    print('from index')

def home():
    time.sleep(1)
    print('from home')

def get_time(xxx):
    start_time = time.time()
    xxx()
    end_time = time.time()
    print('函数执行的时间:', end_time - start_time)
get_time(index)  # from index 函数执行的时间: 3.0078186988830566
get_time(home) # 函数执行的时间: 1.0134220123291016

4.上述代码调用函数只能把函数名传递到其它函数名中当做参数,改变了原函数的调用方式,因此考虑使用闭包函数:
import time


def index():
    time.sleep(3)
    print('from index')


def home():
    time.sleep(1)
    print('from home')
    
def outer(xxx):
    def get_time():
        start_time = time.time()
        xxx()
        end_time = time.time()
        print('函数体代码执行的时间为:', end_time - start_time)

    return get_time


res = outer(index)
res()  # from index 函数体代码执行的时间为: 3.003218412399292
# res = outer(home)  # from home
# res()  # 函数体代码执行的时间为: 1.0004723072052002

5.上述结构函数调用时依然要被当做一个参数将函数名传递给其他函数,依然不符合调用函数的目标。
import time


def outer(xxx):
    def get_time():
        start_time = time.time()
        xxx()
        end_time = time.time()
        print('函数体代码执行的时间为:', end_time - start_time)

    return get_time


index = outer(index)  # res是函数名,可以任意起,当然也可以起index,但是左边的变量名index指向的不是函数体代码了
print(index)  # <function outer.<locals>.get_time at 0x000001A2F2566310> 此时的index指向的是get_time,现在xxx指向的是原来的index代码
index()  # from index 函数体代码执行的时间为: 3.014845371246338

6.上述装饰器只能装饰无参函数,兼容性太差
def func(a):
    time.sleep(0.1)
    print('from func', a)

def func1(a, b):
    time.sleep(0.2)
    print('from func1', a, b)

def func2():
    time.sleep(3)
    print('from funcx')

def outer(xxx):
    def get_time(a, b):
        start_time = time.time()
        xxx(a, b)
        end_time = time.time()
        print('函数体代码执行的时间为:', end_time - start_time)
    return get_time
func1 = outer(func1)
func1(1, 2)
7.内层函数的参数个数要和被装饰的函数个数保持一致,被装饰得函数不知道参数个数,无法兼容
import time

def func(a):
    time.sleep(0.1)
    print('from func', a)

def func1(a, b):
    time.sleep(0.2)
    print('from func1', a, b)

def outer(xxx):
    def get_time(*args, **kwargs):  # 把get_time函数名返回给func时,222会被传给args变成一个元祖
        start_time = time.time()
        xxx(*args, **kwargs)  # 把元组打散成位置参数,传入get_time(func)函数体代码中
        end_time = time.time()
        print('函数体代码执行的时间为:', end_time - start_time)
    return get_time
func = outer(func)
func(222)

8.如果被装饰的函数有返回值,如何返回?
import time

def func(a):
    time.sleep(0.1)
    print('from func', a)
    return 'func'

def func1(a, b):
    time.sleep(0.2)
    print('from func1', a, b)
    return 'func1'

def outer(xxx):
    def get_time(*args, **kwargs):
        start_time = time.time()
        res = xxx(*args, **kwargs)  # 由于此时的func1已经指的是get_time了,所以get_time需要加一个返回值,会返回None,res是把真正的函数名加括号,得到返回值,然后作为get_time的返回值。
        end_time = time.time()
        print('函数体代码执行的时间为:', end_time - start_time)
        return res
    return get_time
func1 = outer(func1)
res = func1(1, 2)
print(res)

6.装饰器模板

1.模板:
def outer(func):
    def inner(*args, **kwargs):
        # 执行被装饰对象之前可以做的额外操作
        res = func(*args, **kwargs)  # 用来接收func的返回值
        # 执行被装饰对象之后可以做的额外操作 ,可有可无
        return res #把原func函数的返回值返回给inner()
    return inner  # 把局部名称空间中的inner返回出去,外面才可以用

2.模板套用:
def func(a):
    time.sleep(0.1)
    print('from func', a)
    return 'func'

def func1(a, b):
    time.sleep(0.2)
    print('from func1', a, b)
    return 'func1'

def func2(a, b ,c):
    time.sleep(0.2)
    print('from func2', a, b, c)
    return 'func2'

def outer(func1):
    def inner(*args, **kwargs):
        print('代码执行前的操作')
        res = func1(*args, **kwargs)
        print('代码执行后的操作')
        return res
    return inner
func1 = outer(func1)
res = func1(1, 2)  # 代码执行前的操作 from func1 1 2 代码执行后的操作
print(res)  # func1  

3.添加身份校验信息:
def func2(a, b ,c):
    time.sleep(0.2)
    print('from func2', a, b, c)
    return 'func2'

def outer(func2):
    def inner(*args, **kwargs):
        user_name = input('请输入您的用户名>>>:')
        user_pwd = input('请输入您的密码>>>:')
        if user_name == 'max' and user_pwd == '123':
            res = func2(*args, **kwargs)
            print('后')
            return res
        else:
            print('用户名或密码错误')
    return inner
func2 = outer(func2)
res = func2(1, 2, 3)
print(res)
"""
如果想要被装饰的函数执行并且有返回值,那么被装饰的函数和普通函数一样都要有函数名加括号、并且赋值给一个变量名,打印。
"""

7.装饰器语法糖

语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@函数点用
def outer(func):
    def inner(*args, **kwargs):
        print('代码执行前的操作')
        res = func(*args, **kwargs)
        print('代码执行后的操作')
        return res
    return inner

@outer
def index():
    print('我是index的函数体代码')
    return '我是index的返回值'
res = index()  # 代码执行前的操作 我是index的函数体代码 代码执行后的操作
print(res)  # 我是index的返回值

8.多层语法糖

def outter1(func1):  # 1.定义outter1()
    print('加载了outter1')  # 12.执行outter1
    def wrapper1(*args, **kwargs):  # 13.定义wrapper1函数
        print('执行了wrapper1')  # 16.执行wrapper函数
        res1 = func1(*args, **kwargs)  # 17.func1参数是wrapper2,调用wrapper2函数
        return res1
    return wrapper1  # 14.返回wrapper1函数名,给到index(语法糖已经走完,此时的index = wrapper1)

def outter2(func2):  # # 2.定义outter2()
    print('加载了outter2')  # 9.执行outter2,参数为wrapper3
    def wrapper2(*args, **kwargs):  # 10.定义wrapper2()函数
        print('执行了wrapper2')  # 18.执行wrapper2函数
        res2 = func2(*args, **kwargs)  # 19.func2指代的是wrapper3,调用wrapper3函数
        return res2
    return wrapper2  # 11.返回wrapper2

def outter3(func3):  # 3.定义outter3()
    print('加载了outter3')  # 5.执行outter3
    def wrapper3(*args, **kwargs):  # 6.定义wrapper3函数
        print('执行了wrapper3')  # 20.执行wrapper3函数
        res3 = func3(*args, **kwargs)  # 21.func3是outter3的参数,根据第一个语法糖得func3是index
        return res3
    return wrapper3  # 7.返回wrapper3函数


@outter1  # 11.将返回给的wrapper2参数给到outter1()函数,执行outter1
@outter2  # 8.将wrapper3返回给outter2,传入函数参数
@outter3  # 4.outter3 = outter3(index),先执行outter3
def index():
    print('from index')  # 22.调用index()函数
index()  # 15.相当于调用wrapper1函数
'''
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
'''

9.有参装饰器

def outer(mode):  # 1.定义outer函数,有一个mode参数
    def login_auth(func):  # 4.定义login_auth函数
        def inner(*args, **kwargs):  # 7.定义inner()函数,判断条件
            print('func',func)
            print('args',args)
            print('kwargs',kwargs)
            if mode == '1':
                print('数据直接直接写死')  # 10.mode是'1',执行这一句
            if mode == '2':
                print('数据来源于文本')
            if mode == '3':
                print('数据来源于字典')
            if mode == '4':
                print('数据来源于mySQL')
            func()  # 11.此时func是index,执行index()

        return inner  # 8.返回函数名inner

    return login_auth  # 5.返回login_auth函数

#  9.此时的语法糖变成了@inner,相当于index=inner(index)
@outer('1')  # 3. 函数名加括号优先级最高,调用outer函数,相当于index=outer('1')  # 6.此时语法糖@outer('1')变成了@login_auth,index=login_auth(index),调用login_auth函数
def index():  # 2.定义index()函数
    print('from index')


index()

10.装饰器模板

常用模板:两层装饰器
def outer(func):
    def inner(*args, **kwargs):
        user_name = input('请输入您的用户名>>>>')
        user_pwd = input('请输入您的密码>>>:')
        if user_name == 'jason' and user_pwd == '123':
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误')
    return inner

@outer
def index():
    print('登录成功')

index()


三层装饰器:外面再套一层mode
def index(mode):
    def outer(func):
        def inner(*args, **kwargs):
            user_name = input('请输入您的用户名>>>:')
            user_pwd = input('请输入您的密码>>>:')
            if user_name == 'jason' and user_pwd == '123':
                res = func(*args, **kwargs)
                return res
        return inner
    return outer

@index('1')  # 函数名加括号执行优先级最高,首先传参给mode,随后编程一个正常的装饰器@outer
def func1():
    print('登陆成功')
func1()

11.递归函数

1.直接调用
def index():
    print('from index')
    index()
index()

2.间接调用
def index():
    print('from index')
    func()

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

index()

3.最大递归深度:python解释器添加的安全措施,官网提供的最大递归深度为1000,测试时出现996-1000都属于正常
count = 0
def index():
    global count
    count += 1
    print(count)
    index()
index()

12.作业装饰器修复技术

# 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()
1.编写一个用户认证装饰器
  函数:register login transfer withdraw
  基本要求
   	 执行每个函数的时候必须先校验身份 eg: jason 123
	 
def outer(func):
    def inner(*args, **kwargs):
        user_name = input('请输入您的用户名>>>:').strip()
        user_pwd = input('请输入您的密码>>>:').strip()
        if user_name == 'jason' and user_pwd == '123':
            print('登陆成功')
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误')
    return inner

@outer
def register():
    print('注册功能')
@outer
def login():
    print('登录功能')
@outer
def transfer():
    print('转账功能')
@outer
def withdraw():
    print('提现功能')

register()
login()
transfer()
withdraw()
	
	
拔高练习(有点难度)
   	 执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
  提示:全局变量 记录当前用户是否认证		
is_login = False


def outer(func):
    def inner(*args, **kwargs):
        global is_login
        if is_login:
            res = func(*args, **kwargs)
            return res
        user_name = input('请输入您的用户名>>>:').strip()
        user_pwd = input('请输入您的密码>>>:').strip()
        if user_name == 'jason' and user_pwd == '123':
            print('登陆成功')
            is_login = True
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误')

    return inner


@outer
def register():
    print('注册功能')


@outer
def login():
    print('登录功能')


@outer
def transfer():
    print('转账功能')


@outer
def withdraw():
    print('提现功能')


register()
login()
transfer()
withdraw()
		
		
2.利用有参装饰器编写多种用户登录校验策略
def login_auth(mode):
    def outer(func):
        def inner(*args, **kwargs):
            user_name = input('请输入您的用户名>>>:').strip()
            user_pwd = input('请输入您的密码>>>:').strip()
            if mode == 'absolute':
                if user_name == 'jason' and user_pwd == '123':
                    print('登陆成功')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('用户名或密码错误')
            elif mode == 'type_list':
                user_list = ['jason|123','kevin|321','tony|222']
                user_data = f'{user_name}|{user_pwd}'
                if user_data in user_list:
                    print('登陆成功')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('登陆失败')
            elif mode == 'type_file':
                with open (r'userinfo.txt', 'r', encoding='utf8') as f1:
                    for line in f1:
                        real_name, real_pwd = line.split('|')
                        if user_name == real_name and user_pwd == real_pwd.strip('\n'):
                            print('登陆成功')
                            res = func(*args, **kwargs)
                            return res
                    else:
                        print('用户名或密码错误')
        return inner
    return outer

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

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

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


# index()
# func()
# foo()


3.利用递归函数依次打印列表中每一个数据值
l1 = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]
l1 = [1,[2,[3,[4,[5,[6,[7,[8,]]]]]]]]
def index(x):
    for i in x:
        if type(i) is int:
            print(i)
        else:
            index(i)
index(l1)
posted @ 2023-04-24 20:29  ERROR404Notfound  阅读(36)  评论(0编辑  收藏  举报
Title