函数的多种用法与装饰器

global与nonlocal

1.global是用在局部名称空间中可以直接修改全局名称空间中的数据
eg:
    a = 123
    def index():
        global a  # 在这里加上global就可以在局部空间中修改全局名称空间中的变量名所绑定的数据值
        a = 111
    index()
    print(a)
     
2.nonlocal是用在内层局部空间,用来修改外层局部空间中的数据
eg:
    def index():
        a = 1
        def inner():
            nonlocal a  # 在这里加上nonlocal就可以在内层局部空间中修改外层局部空间中的数据
            a = 2
        inner()
        print(a)
    
    index()

函数名的多种用法

1.可以当做数据值赋值给变量名
    def index():
        print('123')
    res = index  # 让函数名index所绑定的内存地址也和res绑定
    res()  # 等同于index()
2.可以当做函数的参数
    def index():  # 第一步定义index函数
        print('index')  # 第六步打印index
    def inner(a):  # 第二步定义inner函数
        print(a)  # 第四步打印index所指向的内存地址
        a()  #  第五步执行index函数
    inner(index)  # 第三步执行inner函数,传入参数index
3.可以当做函数的返回值
    def index():  # 第一步定义index函数
        print('index')  # 第九步
    def func():  # 第二步定义func函数
        print('func')  # 第四步
        return index  # 第五步
    res = func()  # 第三步先看赋值符号右边执行func函数  # 第六步用变量名res接收func的返回值index
    print(res)  # 第七步res所指向的内存地址也就是index的内存地址
    res()  # 第八步执行res也就是index函数
4.可以当做容器类型的数据
容器类型就是可以存放多个数据的数据类型
    def register():
        print('注册')
    def login():
        print('登录')
    def delete():
        print('删除')
    func_dict = {
        '1': register, '2': login, '3': delete     # 将函数名作为值存入字典中
    }
    while True:
        print('''
        1.注册
        2.登录
        3.删除
        ''')
        choice = input('请输入功能编号:').strip()
        if choice in func_dict:
            func_name = func_dict.get(choice)  # 找到输入的功能编号对应的函数名引用给func_name
            func_name()
        else:
            print('功能不存在')

闭包函数

定义

定义在函数内部的函数,用到了外部函数名称空间中的变量名
def index():
    a = 1
    def inner():
        print(a)

实际应用

闭包函数其实是一种给函数体代码传参的方式
给函数体代码传参的方式有两种
方式一:缺什么补什么
    代码里面缺什么变量名形参里面就补什么变量名
    eg:
        def register(a, b):
            print(a, b)
        register(1, 2)
        
方式二:闭包函数
    def outer(a, b):  # 1.定义函数outer
        def register():  # 3.定义函数register
            print(a, b)  # 6.打印a b
        return register  # 4.返回值为register
    res = outer(1, 2)  # 2.执行函数outer并将返回值赋值给res  
    res()  # 5.执行res也就是register

装饰器简介

1.概念
    在不改变被装饰对象源代码和调用方式的情况下给被装饰对象添加新的功能
2.本质
    其实就是将函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果
3.口诀
    对修改封闭,对扩展开放
4.储备知识
    时间相关操作
    import time
    print(time.time())  # 时间戳,距离1970.1.1 00:00:00所经历的秒数
    time.sleep(3)  # 程序在此停止多长时间
    print()

装饰器推导流程

有这样一段代码:
import time

def index():
    time.sieep(2)  # 代码在此停顿2秒
    print('index')
def home():
    time.sleep(1)  # 代码在此停顿一秒
    print('home')
需求:打印函数index的执行时间

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

start_time = time.time()  # 记录index函数执行开始时间
index()
end_time = time.time()  # 记录index函数执行结束时间
print('函数index的执行时间为:',end_time - start_time)

2.当需要频繁调用index函数时,使用第一步的方法太繁琐,可以优化封装成函数

def get_time():
    start_time = time.time()
    index()
    end_time = time.time()
    print('函数index的执行时间为:',end_time - start_time)
get_time()

3.第二步封装成函数以后,可以直接调用get_time函数来完成需求,但是只能统计index的执行时间,不能统计其他函数的执行时间,基于此我们可以把get_time改成有参函数

def get_time(a):  # 定义函数get_time位置参数a
    start_time = time.time()
    a()  # 执行传入的函数
    end_time = time.time()
    print('函数index的执行时间为:',end_time - start_time)
get_time(index)  # 执行函数get_time,传入参数index函数
get_time(home)  # 执行函数get_time,传入参数home函数

4.经过第三步的修改以后实现了一定的兼容性,但是不符合装饰器的特征,所以在这里我们可以考虑闭包函数

def outer(a):
    def get_time():
        start_time = time.time()
        a()  # 执行传入的函数,从outer里面获取参数
        end_time = time.time()
        print('函数index的执行时间为:',end_time - start_time)
    return get_time
index = outer(index)  # 将函数outer的返回值赋值给index
index()  # 此时的index不是原来的index,而是函数outer执行之后的返回值get_time
home = outer(home)
home()

5.第四步符合了装饰器的特征,但是只能装饰无参函数,需要将其修改为可以装饰有参函数,但是考虑到有参函数传入的参数数量类型未知,我们可以使用可变长参数来修改

def outer(a):
    def get_time(*args, **kwargs):  # 有参函数传入的参数数量类型未知,所以使用可变长参数来接收
        start_time = time.time()
        a(*args, **kwargs)  # 以get_time的形参作为a的实参,所以get_time接收到什么数据就传入a什么数据
        end_time = time.time()
        print('函数index的执行时间为:',end_time - start_time)
    return get_time
index = outer(index)  # 将函数outer的返回值赋值给index
index()  # 此时的index不是原来的index,而是函数outer执行之后的返回值get_time

def index1(a):
    time.sleep(1.5)
    print('index1', a)
    
index1 = outer(index1)
index1(5)

def index2(a, b, c):
    time.sleep(2.5)
    print('index2', a, b, c)
    
index2 = outer(index2)
index2(7, 8, 9)

6.如果被装饰的对象有返回值,那么我们需要想办法将返回值也传递出来

def outer(a):
    def get_time(*args, **kwargs):
        start_time = time.time()
        res = a(*args, **kwargs)
        end_time = time.time()
        print('函数index的执行时间为:',end_time - start_time)
        return res
    return get_time

def func(a, b):
    time.sleep(1.8)
    print('func', a, b)
    return a + b

func = outer(func)
res = func(123, 321)
print(res)

装饰器模板

def outer(func):
    def inner(*args, **kwargs):
        '''执行被装饰对象之前可以进行的额外操作'''
        res = func(*args, **kwargs)
        '''执行被装饰对象之后可以进行的额外操作'''
        return res
    return inner

装饰器语法糖

基于上面的装饰器模板,每次使用都需要做一个操作func = outer(func)
语法糖可以将这一步简化为@outer
使用方法就是
@outer  # 语法糖会将下面紧挨着的函数名当做第一个参数传给@后面的函数调用,即 func = outer(func)
def func():
    print('from func')
    return 'func'

func()  # 此时执行的函数func是经过装饰器装饰过的

作业

编写一个用户认证装饰器
  函数:register login transfer withdraw 
  基本要求
   	 执行每个函数的时候必须先校验身份 eg: jason 123
  拔高练习(有点难度)
   	 执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
  提示:全局变量 记录当前用户是否认证
a= [False]
print(type(a))
def attestation(func):
    def attes(*args, **kwargs):
        if a[0]:
            res = func(*args, **kwargs)
            return res
        name = input('请输入用户名:')
        age = input('请输入密码:')
        if name == 'jason' and age == '123':
            res = func(*args, **kwargs)
            a[0] = True
            return res
        else:
            print('用户名密码错误')

    return attes


@attestation
def register():
    print('注册成功')


@attestation
def login():
    print('登录成功')


@attestation
def transfer():
    print('转账成功')


@attestation
def withdraw():
    print('提现成功')


attes_list = {
    '1': register,
    '2': login,
    '3': transfer,
    '4': withdraw
}


while True:
    print('1.注册功能\n2.登录功能\n3.转账功能\n4.提现功能')
    choice = input('请键入您要选择的功能:').strip()
    if choice in attes_list:
        attes1 = attes_list.get(choice)
posted on 2023-04-05 19:44  zyg111  阅读(29)  评论(0)    收藏  举报