函数(三)——闭包函数与装饰器

一、函数名的多种用法

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

1. 可以当做变量名赋值

def index():pass
     res = index
     res()

2. 可以当做函数的参数

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


def func(a):    #
    print(a)    # ④ 执行func函数的子代码,a = index >>>  <function index at 0x00000197298A73A0>
    a()    # ⑤ a()相当于index(),调用index函数


func(index)    # ③ 调用func函数

 

3. 可以当做函数的返回值

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


def func():    #
    print('from func')    # ④ 执行func函数体代码 from func
    return index     #


res = func()  # ③右边调用func函数  ⑥左边接受index的返回值 res = index
print(res)    # res的函数体代码  <function index at 0x0000029815CF73A0>
res()    # ⑦ res()相当于index(),调用index函数  from index
def index():  # 1
    print('from index')  # 3.执行index函数体代码   from index

    def func():  # 4
        print('from func')    # 8. 执行func函数体代码  from func

    return func  # 5. 返回func


res = index()  # 2.右边调用index函数    6. res接受func的返回值,res = func

print(res)  # 打印func的函数体代码  <function index.<locals>.func at 0x000002602F357700>
res()  # 7.res()相当于func(),调用func函数

 

4. 可以当做容器类型(可以存放多个数据的数据类型)的数据

例:要通过不同的编号实现注册、登录、提现、转账、购物等功能

方法1:

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


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


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


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


def shopping():
    print('购物功能')
#
while True:
    print("""
    1.注册功能
    2.登录功能
    3.提现功能
    4.转账功能
    5.购物功能
    """)
    choice = input('>>>:').strip()
    if choice == '1':
        register()
    elif choice == '2':
        login()
    elif choice == '3':
        withdraw()
    elif choice == '4':
        transfer()
    elif choice == '5':
        shopping()
    else:
        print('其他')

该方法比较麻烦,有多少种功能就得有多少个选择分支。

方法2:

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:
    print("""
    1.注册功能
    2.登录功能
    3.提现功能
    4.转账功能
    5.购物功能
    """)
    choice = input('>>>:').strip()
    if choice in func_dict:
        func_name = func_dict.get(choice)
        func_name()
    else:
        print('功能编号不存在')

把函数名放到一个字典里,通过输入的数值提取数值对应的键的值,即函数名,函数名加括号即可调用相应的函数。

 

二、闭包函数

什么是闭包函数:

    1.定义在函数内部
    2.用到外部函数名称空间中的名字

 

闭包函数实际应用

  是另外一种给函数体代码传参的方式

给函数体代码传参的方式1

  代码里面缺什么变量名形参里面就补什么变量名

def register(name,age):
    print(f"""
    姓名:{name}
    年龄:{age}
    """)
register('jason', 18)

给函数体代码传参的方式2:闭包函数

def outer(name, age):
    # name = 'jason'
    # age = 18
    def register():
        print(f"""
        姓名:{name}
        年龄:{age}
        """)
    return register
res = outer('jason', 18)
res()     # jason, 18
res()     # jason, 18
res = outer('kevin', 28)
res()     # kevin, 28
res()     # kevin, 28

 

三、装饰器简介

1.什么是装饰器:

装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

2. 装饰器的原则:

    1) 不修改被装饰对象的源代码

    2) 不修改被装饰对象的调用方式

3.本质

并不是一门新的技术,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果,目的是为被装饰对象添加上新功能。

4.口诀

对修改封闭,对扩展开放

5.储备知识

时间相关操作

import time
print(time.time())  # 时间戳(距离1970-01-01 00:00:00所经历的秒数)
time.sleep(3)
print('睡醒了 干饭')
count = 0
循环之前先获取时间戳
start_time = time.time()
while count < 100:
    print('哈哈哈')
    count += 1
end_time = time.time()
print('循环消耗的时间:', end_time - start_time)

 

四、装饰器推导:

被装饰者:

import time


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

要统计index函数执行的时间:

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

import time


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

'''1. 直接在调用index函数的前后添加代码'''
start_time = time.time()
index()
end_time = time.time()
print('函数的执行时间>>>:',end_time-start_time)

 

2. index调用的地方较多,代码不可能反复拷贝>>>:相同的代码需要在不同的位置反复执行>>>:函数

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


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


get_time()

 

3.上述函数体代码写死了,只能统计index的执行时间,如何才能做到统计更多的函数运行时间>>>:直接传参变换统计的函数

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


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


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


get_time(index)
get_time(home)

4. 上述虽然实现了一定的兼容性,但是并不符合装饰器的特征

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

def home():
    time.sleep(3)
    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()
res1 = outer(home)
res1()

5. 调用方式还是不对,如何变形>>>:变量名赋值绑定

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

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

def outer(xxx):    # xxx真正的index函数指向的index函数名绑定的内存地址
    def get_time():
        start_time = time.time()
        xxx()
        end_time = time.time()
        print('函数的执行时间>>>:', end_time - start_time)
    return get_time

# 左边的变量名可以为任意的,所以我们也可以让它与要统计的函数名称一致,但是它接收的是get_time函数的内存地址
index = outer(index)    # 括号里面为真正的index函数
index()
home = outer(home)
home()

6.上述装饰器只能装饰无参函数,兼容性性太差

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

def outer(xxx):    # xxx真正的index函数指向的index函数名绑定的内存地址
    def get_time(a):
        start_time = time.time()
        xxx(a)
        end_time = time.time()
        print('函数的执行时间>>>:', end_time - start_time)
    return get_time

func = outer(func)
func(1)

7. 被装饰的函数不知道有没有参数以及有几个参数,如何兼容

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

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

def outer(xxx):    # xxx真正的index函数指向的index函数名绑定的内存地址
    def get_time(*args,**kwargs):
        start_time = time.time()
        xxx(*args,**kwargs)
        end_time = time.time()
        print('函数的执行时间>>>:', end_time - start_time)
    return get_time

func = outer(func)
func(1)

func1 = outer(func1)
func1(1,2)

 8.如果被装饰的函数有返回值

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

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

def outer(xxx):    # xxx真正的index函数指向的index函数名绑定的内存地址
    def get_time(*args,**kwargs):
        start_time = time.time()
        res = xxx(*args,**kwargs)
        end_time = time.time()
        print('函数的执行时间>>>:', end_time - start_time)
        return res
    return get_time

func = outer(func)
res = func(1)
print(res)

func1 = outer(func1)
func1(1,2)

六、多层语法糖

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

def outer1(func1):    # 1
    print('加载了outer1')    # 14. 执行outer1,'加载了outer1'

    def wrapper1(*args, **kwargs):    # 15
        print('执行了wrapper1')     # 18. 执行wrapper1的函数体代码,’执行了wrapper1‘
        res1 = func1(*args, **kwargs)    # 19. func1为outer1的参数,第13步中outer1接受了wrapper2的返回值,func1 = wrapper2,func1()相当于wrapper2(),调用wrapper2
        return res1

    return wrapper1    # 16. 返回wrapper1, 传递给index, index = wrapper1


def outer2(func2):    # 2
    print('加载了outer2')    # 10. 执行outer2, '加载了outer2'

    def wrapper2(*args, **kwargs):    # 11
        print('执行了wrapper2')    # 20. 执行wrapper2, '执行了wrapper2'
        res2 = func2(*args, **kwargs)    # 21. func2为outer2的参数,第9步中outer2接受了返回的wrapper3,func2 = wrapper3, func2()相当于wrapper3(),调用wrapper3
        return res2

    return wrapper2    # 12. 返回wrapper2, 传递给语法糖上面的outer1


def outer3(func3):    # 3
    print('加载了outer3')    # 6.执行outer3的函数体代码,‘加载了outer3'

    def wrapper3(*args, **kwargs):    # 7
        print('执行了wrapper3')    # 22. 执行wrapper3, '执行了wrapper3'
        res3 = func3(*args, **kwargs)    # 23. func3为outer3的参数,outer3接受了index, func3 = index, func()相当于index(), 调用index
        return res3

    return wrapper3    # 8.返回wrapper3,传递给语法糖上面的outer2


@outer1    # 13. outer1接受wrapper2的返回值,outer1(wrapper2),调用outer1
@outer2    # 9. outer2接受返回的wrapper3, outer2(wrapper3),调用outer2
@outer3    # 5 index传递给outer, outer3(index),调用outer3
def index():    # 4
    print('from index')    # 24. 执行index的函数体代码,’from index‘
index()    # 17. index()相当于wrapper1(),调用wrapper1函数

执行顺序:

加载了outer3
加载了outer2
加载了outer1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index

 

七、有参装饰器

当装饰器中需要额外的参数时,需要有参装饰器。

例:校验用户是否登录装饰器

def outer(mode):
    def login_outer(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_outer

 

八、装饰器模板

1. 最常用的无参装饰器

def outer(func_name):
    def inner(*args, **kwargs):
        res = func_name(*args, **kwargs)
        return res
    return inner
@outer
def index():
    pass

 

2. 不常用的有参装饰器

def outer_plus(mode):
    def outer(func_name):
        def inner(*args, **kwargs):
            res = func_name(*args, **kwargs)
            return res
        return inner
    return outer
@outer_plus('MySQL')
def func():
    pass

 

九、装饰器修饰技术

通过添加固定的代码可以使得装饰器的效果更加逼真,让被装饰对象更加不容易察觉被装饰了,不是必须要写的。

修饰前:

def outer(func_name):

    def inner(*args, **kwargs):
        """我是inner 我擅长让人蒙蔽"""
        res = func_name(*args, **kwargs)
        return res
    return inner

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


help(func)    # func绑定的是inner,help找到的也是inner的相关内容
print(func)
func()

 

修饰后:

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)   # help查到的是func的内容
print(func)
func()

 

posted @ 2022-10-11 20:56  莫~慌  阅读(450)  评论(0编辑  收藏  举报