28.Python装饰器&语法糖的使用

1.装饰器

作用:对函数进行装饰,添加新的功能。

原则:开发封闭原则,对扩展开放,对修改封闭。
在不改变被装饰对象'内部代码'以及'调用方式'的基础上添加新的功能。
被装饰:函数。

2.主体功能

为函数添加一个记录执行时间的功能。
time.sleep(阻塞的秒数)  
time.time()获取时间戳
197011000秒到现在的秒数
获取两次时间戳 
时间多的(后一次) - 时间少的(前一次)= 程序执行时间
# 显示实现主体功能
def func1():
    print('程序开始执行···')
    time.sleep(1)  # 程序阻塞1秒
    print('程序执行结束···')


# 在函数调用之前获取一个时间戳
start_time = time.time()
func1()
end_time = time.time()
print('程序执行时间为:%s' % (end_time - start_time))
# 在函数执行结束获取一个时间戳

3.演变1

为每个函数都统计时间,则将获取获取时间戳的代码写成一个函数,在需要的时候调用。
1.在需要要重复使用的代码块在面定义函数 def 函数名 写上一个参数
2.将下面代码块缩进
3.将函数名改为一个变量名与参数对应
4.调用测试时间函数的时候,将被测试的函数名作为实参。
5.运行程序

核心:将测试的函数名传入函数中,在函数中执行。

image-20211117152849458

# 导入 time模块
import time


def func1():
    print('程序开始执行···')
    time.sleep(1)  # 程序阻塞1秒
    print('程序执行结束···')

def get_time(func):
    # 在函数调用之前获取一个时间戳
    start_time = time.time()
    func()
    end_time = time.time()
    print('程序执行时间为:%s' % (end_time - start_time))
    # 在函数执行结束获取一个时间戳

get_time(func1)
缺点: 修改了函数的调用方式,不符合装饰器的定义。

4演变2

之前是需要让额外的功能可以作用于任何的函数,
方式1:将被装饰的函数名当作一个参数传入函数中,在函数中调用执行。
这个传参的方式改变了调用的方式。

方式2:使用闭包函数来传参,这个样就解决了函数调用方式问题。
闭包函数是给函数传值的另一种方式。

在这里插入图片描述

def func1():
    print('程序开始执行···')
    time.sleep(1)  # 程序阻塞1秒
    print('程序执行结束···')


def time_func(func):
    def get_time():
        # 在函数调用之前获取一个时间戳
        start_time = time.time()
        func()
        end_time = time.time()
        print('程序执行时间为:%s' % (end_time - start_time))
        # 在函数执行结束获取一个时间戳
    return get_time


func1 = time_func(func1)
func1()
*** 偷梁换柱 ***
执行time_func()时将被装饰的函数名作为实参绑定给func,
然后在将get_time()函数名放回绑定给func1。

执行func1 其实是在执行get_time(), func()执行的是func1()

image-20211117163027010

缺点:无法传递参数。

5.演变3

*args **kwargs可以接受任何参数,也可以满足不传参数的函数。
func函数提供参数 ---> get_time函数 --->  func1函数需要参数。

image-20211117170946466

# 导入 time模块
import time


def func1(num):
    print('程序开始执行···')
    time.sleep(1)  # 程序阻塞1秒
    print(num)
    print('程序执行结束···')


def time_func(func):
    def get_time(*args, **kwargs):  
        # 在函数调用之前获取一个时间戳
        start_time = time.time()
        func(*args, **kwargs)         
        end_time = time.time()
        print('程序执行时间为:%s' % (end_time - start_time))
        # 在函数执行结束获取一个时间戳
    return get_time


func1 = time_func(func1)
func1(1)
缺点:没有返回值。

6.演变4

获取函数执行后的返回值。
get_time 中接收func1 中return 返回值---> 并赋值给res, 
在将res 做为 get_time函数的返回值,在调用时get_time时接收。
这时的func其实就是get_time。

image-20211117172521823

# 导入 time模块
import time


def func1(num):
    print('程序开始执行···')
    time.sleep(1)  # 程序阻塞1秒
    print(num)
    print('程序执行结束···')
    return 1, 2, 3


def time_func(func):
    def get_time(*args, **kwargs):
        # 在函数调用之前获取一个时间戳
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print('程序执行时间为:%s' % (end_time - start_time))
        # 在函数执行结束获取一个时间戳
        return res

    return get_time


func1 = time_func(func1)
res = func1(1)
print(res)  # (1, 2, 3)

7.练习

验证装饰器。当信息校验成功后才能真常执行函数。
def login(func):
    def inner1(*args, **kwargs):
        # 用户登入
        name = input('用户名输入>>>:')
        pwd = input('用户密码输入>>>:')
        if name == 'kid' and pwd == '123':
            res = func()
            return res
        else:
            print('密码错误')

    return inner1


def index():
    print('程序执行···')
    
index = login(index)
index()

8.装饰器模板

def outer(func):
    def inner(*args, **kwargs):
        # 函数执行前的功能
        res = func(*args, **kwargs)
        # 函数执行后的功能
        return res

    return inner

9.语法糖

装饰器语法糖书写规范:
	语法糖必须紧贴在装饰对象
装饰器语法糖内部原理:
	会自动将下面紧贴着的被装饰对象名字当作参数传递给装饰器函数调用
def outer(func):
    def inner(*args, **kwargs):
        print('程序开始执行···')
        res = func(*args, **kwargs)
        print('程序执行结束···')
        return res

    return inner


@outer  # 等同于 index = outer(index)
def index():
    print('程序执行···')


index()

10.双层语法糖

def login(func):
    def inner1(*args, **kwargs):
        # 用户登入
        name = input('用户名输入>>>:')
        pwd = input('用户密码输入>>>:')
        if name == 'kid' and pwd == '123':
            res = func()
            return res
        else:
            print('密码错误')

    return inner1


def outer(func):
    def inner2(*args, **kwargs):
        print('程序开始执行···')
        res = func(*args, **kwargs)
        print('程序执行结束···')
        return res

    return inner2

    

        # index = inner1
@login  # login(inner2)---> inner1
@outer  # outer(index) ---> inner2
def index():
    print('程序执行···')


index()

image-20211117230117622

11.装饰器修复

修复技术是为了让被装饰对象更加不容易被察觉被装饰了。
print()可以看到内置地址
help(函数名可以看到函数的基本信息) 
11.1修复前
def outer(func):
    def inner(*args, **kwargs):
        print('程序开始执行···')
        res = func(*args, **kwargs)
        print('程序执行结束···')
        return res

    return inner


@outer
def index():
    print('程序执行···')



help(index)

"""
<function outer.<locals>.inner at 0x0000019FB3A82D90>
Help on function inner in module __main__:

inner(*args, **kwargs)

"""
11.2修复后
from functools import wraps
def outer(func):
    @wraps(func)  # 这个func为index 将index的信息保存下来后 覆盖 inner的信息
    def inner(*args, **kwargs):
        print('程序开始执行···')
        res = func(*args, **kwargs)
        print('程序执行结束···')
        return res

    return inner


@outer
def index():
    print('程序执行···')


print(index)
help(index)

"""
<function index at 0x0000024A4FED2D90>
Help on function index in module __main__:

index()
"""

12.三层装饰器

   定义阶段            调用阶段
          @outter1     
          @outter2     
          @outter3     

在这里插入图片描述
image-20211231232148559

def outter1(func1):
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2):
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3):
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

          # index = wrapper1
@outter1  # outter1(wrapper2) ---> wrapper1
@outter2  # outter2(wrapper3) ---> wrapper2
@outter3  # outter3(index)  ---> wrapper3
def index():  # wrapper1()
    print('from index')

13.有参装饰器

@函数(参数)

@outer('file')分三步执行:
1.将括号内的参数先传给 outer()  source_data
2.第二步@outer  index作为参数传给 login_auth() 的func
3.将login_auth绑定给index

在调用index其实就是在调用 login_auth函数。

login_auth  auth 的参数是固定的不能在写入参数,只能在写一个外层的局部。
最里层的参数问最外层的函数拿值。
def outer(source_data):
    # source_data = 'file'
    def login_auth(func):
        def auth(*args,**kwargs):
            # 2.校验用户名和密码是否正确
            # 数据的校验方式可以切换多种
            if source_data == 'file':
                # 从文件中获取用户数据并比对
                print('file文件获取')
            elif source_data == 'MySQL':
                # 从MySQL数据库中获取数据比对
                print('MySQL数据库获取')
            elif source_data == 'postgreSQL':
                # 从postgreSQL数据库中获取数据对比
                print('postgreSQL数据库获取')
            else:
                print('用户名或密码错误 无法执行函数')
        return auth
    return login_auth

@outer('file')
def index():
    print('from index')

index()

14.有参装饰器模板

# 无参装饰器  使用语法糖传参数
def dic(vip):
    def outter(func):
        def werapper(*args, **kwargs):
            ser = func(*args, **kwargs)
            return ser
        return werapper
    return outter

@dic(vip='')
def index(x):
    ...

15.练习

1.编写函数,(函数执行的时间用time.sleep(n)模拟)
import time


def func():
    print('函数开始执行!')
    time.sleep(2)
    print('函数执行结束!')


start_time = time.time()
func()
end_time = time.time()
print('程序执行了%0.2f秒' % (end_time - start_time))
2.编写装饰器,为函数加上统计时间的功能
import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print('程序执行了%0.2f秒' % (end_time - start_time))
        return res

    return inner


@outer
def func1():
    print('函数开始执行!')
    time.sleep(2)
    print('函数执行结束!')
3.编写装饰器,为函数加上认证的功能
import time


def outer(func):
    def inner(*args, **kwargs):
        name = input('输入用户名称>>>:').strip()
        pwd = input('输入用户密码>>>:').strip()
        if name == 'kid' and pwd == '123':
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误!')

    return inner


@outer
def func1():
    print('函数开始执行!')
    time.sleep(2)
    print('函数执行结束!')


func1()
4.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,
后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"kid","paw":"123"}')转成字典格式
import time

# 定义一个记录登录的变量
login = {
    'is_login': False
}


def outer(func):
    def inner(*args, **kwargs):
        if login['is_login']:
            res = func(*args, **kwargs)
            return res
        else:
            name = input('输入用户名称>>>:').strip()
            pwd = input('输入用户密码>>>:').strip()

            with open('db.txt', mode='rt', encoding='utf8') as rf:
                info_str = rf.read()
                info_dic = eval(info_str)
            if name == info_dic['name'] and pwd == info_dic['pwd']:
                res = func(*args, **kwargs)
                # 修改登入状态
                login['is_login'] = True
                return res

            else:
                print('用户名或密码错误!')

    return inner


@outer
def func1():
    print('函数1开始执行!')
    time.sleep(2)
    print('函数1执行结束!')


@outer
def func2():
    print('函数2开始执行!')
    time.sleep(2)
    print('函数2执行结束!')


@outer
def func3():
    print('函数3开始执行!')
    time.sleep(2)
    print('函数3执行结束!')


# 函数名做容器容器类型的值元素
func_dic = {
    '1': func1,
    '2': func2,
    '3': func3,
}

while True:

    choice = input('输入编号>>>:')

    if choice in func_dic:
        func_dic[choice]()  # 加括号调用
    else:
        print('输入的编号不存在!')
5.编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
import time

# 定义一个记录登录的变量
login = {
    'is_login': False,  # 登入状态
    'limit': 0.0
}
# 超时的时间 x秒
time_out = 15

def outer(func):
    def inner(*args, **kwargs):
        if login['limit']:  # 为0.0 的时候就没有登入过 什么都不去执行, 不为0.0就说明登入过修改了时间
            # 过时
            is_obsolete = False
            now_time = time.time()
            if (now_time - login['limit']) < time_out:
                is_obsolete = True
			# 有用户登入的时候,在每超时时直接可以执行程序
            if login['is_login'] and is_obsolete:
                res = func(*args, **kwargs)
                return res

        name = input('输入用户名称>>>:').strip()
        pwd = input('输入用户密码>>>:').strip()

        with open('db.txt', mode='rt', encoding='utf8') as rf:
            info_str = rf.read()
            info_dic = eval(info_str)
        if name == info_dic['name'] and pwd == info_dic['pwd']:
            res = func(*args, **kwargs)
            # 修改登入状态

            login['is_login'] = True
            start_time = time.time()
            login['limit'] = start_time
            return res

        else:
            print('用户名或密码错误!')

    return inner


@outer
def func1():
    print('函数1开始执行!')
    time.sleep(2)
    print('函数1执行结束!')


@outer
def func2():
    print('函数2开始执行!')
    time.sleep(2)
    print('函数2执行结束!')


@outer
def func3():
    print('函数3开始执行!')
    time.sleep(2)
    print('函数3执行结束!')


func_dic = {
    '1': func1,
    '2': func2,
    '3': func3,
}

while True:

    choice = input('输入编号>>>:')

    if choice in func_dic:
        func_dic[choice]()
    else:
        print('输入的编号不存在
6.叠加多个装饰器,画图标出加载顺序与运行顺序
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值