装饰器--从入门到入土!

2022-03-25 20:09:56 浏览数 (1)

目录

  • 装饰器
    • 1、定义
    • 2、原则
    • 3、装饰器核心思想
    • 4、装饰器简易版本
    • 5、解决有参函数问题
    • 6、解决返回值问题
    • 7、装饰器模板
    • 8、认证装饰器
    • 9、装饰器语法糖
      • 单层语法糖(重难点)
      • 双层语法糖(重难点)
      • 三层语法糖(难点)
    • 10、装饰器修复技术
    • 11、有参装饰器

装饰器

本文讲述了装饰器由简易装饰器到完整装饰器的过程,一个段落解决一个问题!

1、定义

  • 器:指的是工具
  • 装饰:给被装饰对象添加额外的功能

2、原则

开放封闭的原则

  • 开放:对扩展开放
  • 封闭:对修改封闭

3、装饰器核心思想

在不改变被“装饰”对象内部代码原有调用方式的基础之上添加额外的功能。

实例如下:

代码语言:javascript复制
# 计算程序的执行时间
import time

# 获取的结果叫时间戳(运行代码的那一刻距离1970-1-1所经历的秒数)
print(time.time())


def index():
    time.sleep(3)
    print('计算时间停止了3秒后的结果!')


# 统计开始时间
start_time = time.time()
index()
# 统计结束的时间
end_time = time.time()
# 输出时间差
print(end_time-start_time)
# 时间运行的时间
print(end_time-start_time-3)

4、装饰器简易版本

实例如下: 使用闭包函数实现

代码语言:javascript复制
import time
# 定义普通无参函数
def func():
    time.sleep(2)
    print('正在执行func函数')


def get_func_name(func_name):
    # 将参数写入形参,可以灵活传参
    # func_name = func
  
   # 构造一个能够计算函数运行时间的闭包函数
    def get_time():
    # 获取开始时间
        start_time = time.time()
        # 调用函数
        func_name() # func_name()就是func()
        # 查看func_name指向的是哪个函数,方便理解
        print('func_name指向的是>>>:',func_name)
        # 获取结束时间
        end_time = time.time()
        # 打印运行时间
        print("函数执行时间为{0}".format(end_time-start_time))
    # 获取返回值
    print('get_time指向的是>>>:',get_time)
    return get_time  # 这里其实是把func

# 调用get_func_name函数,传参,res接收返回结果,返回值为get_time
res = get_func_name(func)
res()
# 获取res指向的是那个函数
print('res指向的是>>>:',res)

5、解决有参函数问题

由于简易版本的装饰器,只能装饰无参函数,为了让有参函数也能够被装饰,需要改进简易版本 实例如下:

代码语言:javascript复制
import time
# 定义一个无参函数
def non_argument():
    time.sleep(1)
    print('执行的是无参函数')
# 定义一个有参函数
def have_argument(*args,**kwargs):
    time.sleep(1)
    print('执行的是有参函数,参数为:',*args,**kwargs)

def get_func_name(func_name):
    # func_name = non_argumengt
    def get_time(*args,**kwargs):
        start_time = time.time()
        func_name(*args,**kwargs)
        end_time = time.time()
        print('函数运行时间为:',end_time-start_time)
    return get_time

#调用无参函数
non_argument = get_func_name(non_argument)
non_argument()  # 这里的non_argument()其实指向的是get_time
# 打印查看一下,指向的是谁,此non_argument()非彼non_argument
print(non_argument)

# 调用有参函数
have_argument = get_func_name(have_argument)
have_argument('hammer',18)

6、解决返回值问题

为了解决能够装饰有返回值的有参、无参函数,计算它们的运行时间,升级装饰器为可以装饰有参、无参带返回值的装饰器,改进装饰器版本。 实例如下:

代码语言:javascript复制
import time
# 定义一个无参函数
def non_argument():
    time.sleep(1)
    print('执行的是无参函数')
    return 'from non_argument'
# 定义一个有参函数
def have_argument(*args,**kwargs):
    time.sleep(1)
    print('执行的是有参函数,参数为:',*args,**kwargs)
    return 'from have_argument'

# 现在可以计算有参、无参、有返回值的函数执行时间
def get_func_name(func_name):   # func_name = non_argumengt

    def get_time(*args,**kwargs):
        start_time = time.time()
        # 用get_return 接收被装饰函数的返回值
        get_return = func_name(*args,**kwargs)
        end_time = time.time()
        print('函数运行时间为:',end_time-start_time)
        # 返回被装饰函数的返回值
        return get_return
    return get_time

# 调用装饰器,打印被装饰函数的运行时间,和返回值
non_argument = get_func_name(non_argument)
print(non_argument())

have_argument = get_func_name(have_argument)
print(have_argument())

返回值解决后,就会推出一个固定的格式,外层获取函数名不能改变,内层获取被装饰的参数( * args, * kwargs)不用改变。*

7、装饰器模板

装饰器经过多版本的升级,发现了一个规律,推出装饰器模板 实例如下:

代码语言:javascript复制
def get_func_name(func_name):   # func_name = 函数名

    def get_time(*args,**kwargs):
        # 写入被装饰前的代码体
        '''代码体'''
        # 用get_return 接收被装饰函数的返回值
        get_return = func_name(*args,**kwargs)
        # 写入被装饰后的代码体
        '''代码体'''
        # 返回被装饰函数的返回值
        return get_return
    return get_time

8、认证装饰器

简易版认证装饰器:

代码语言:javascript复制
'''
在调用sign之前需要用户输入用户名和密码
    正确才可以调用
    错误直接拒绝
'''
import time


def sign():
    time.sleep(1)
    print('sign is running')
    return 'from sign'


def get_func_name(func_name):
    def get_func_argument(*args, **kwargs):
        username = input('please input your name>>>:').strip()
        password = input('please input your pwd>>>:').strip()
        if username == 'hammer' and password == '123':
            get_return = func_name(*args, **kwargs)
            print('>>>>>success!')
        else:
            print('>>>>>failure!')
        return get_return

    return get_func_argument


sign = get_func_name(sign)
print(sign())

多函数复杂认证装饰器:认证一个函数后面的函数不需要认证 实例如下:

代码语言:javascript复制
import time


def sign():
    time.sleep(1)
    print('sign is running')
    return 'from sign'
def login():
    time.sleep(1)
    print('login is running')
    return 'from login'
def shopping():
    time.sleep(1)
    print('shopping is running ')
    return 'from shopping'


# 定义一个用于记录用户是否登录的数据,全局标志
is_login = {'is_login':False}
# 构造一个装饰器
def get_func_name(func_name):
    def get_func_argument(*args, **kwargs):
        # 判断用户是否登录
        # 登录过就直接运行被装饰的函数
        if is_login.get('is_login'):
            get_return = func_name(*args, **kwargs)
            print('>>>>>success!')
            return get_return

        username = input('please input your name>>>:').strip()
        password = input('please input your pwd>>>:').strip()
        if username == 'hammer' and password == '123':
            get_return = func_name(*args, **kwargs)
            # 用户如果登录成功将登录状态改成True
            is_login['is_login']= True
            print('>>>>>success!')
            return get_return
        else:
            print('>>>>>failure!')
        # return get_return
    return get_func_argument


sign = get_func_name(sign)
sign()

login = get_func_name(login)
login()

shopping = get_func_name(shopping)
shopping()

9、装饰器语法糖

格式:@装饰器 原理:会将下面紧贴的函数当成被装饰的对象 语法糖执行顺序:****被装饰的对象如果有多层语法糖,装饰的顺序由下向上

实例如下: eg:# @get_func_name 相当于 函数名()= get_func_name(函数名)

单层语法糖(重难点)

代码语言:javascript复制
import time
# 现在可以计算有参、无参、有返回值的函数执行时间
def get_func_name(func_name):   # func_name = non_argumengt

    def get_time(*args,**kwargs):
        start_time = time.time()
        # 用get_return 接收被装饰函数的返回值
        get_return = func_name(*args,**kwargs)
        end_time = time.time()
        print('函数运行时间为:',end_time-start_time)
        # 返回被装饰函数的返回值
        return get_return
    return get_time


# 定义一个无参函数
@get_func_name
# @get_func_name 相当于 函数名= get_func_name(函数名)  
def non_argument():
    time.sleep(1)
    print('执行的是无参函数')
    return 'from non_argument'


non_argument()

双层语法糖(重难点)

实例如下:

代码语言:javascript复制
# 统计函数运行时间
import time
def get_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)  # 执行被装饰的函数
        end_time = time.time()
        print('函数执行时间:%s'%(end_time-start_time))
        return res  # 将被装饰函数执行之后的返回值返回
    return inner
# 校验用户登录装饰
def login_auth(func):
    def inner(*args, **kwargs):
        # 1.先获取用户的用户名和密码
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        # 2.校验用户名和密码是否正确
        if username == 'jason' and password == '123':
            res = func(*args, **kwargs)  # 执行被装饰的函数
            return res  # 将被装饰函数执行之后的返回值返回
        print('用户名或密码错误 无权限执行')
    return inner
@login_auth
@get_time # get_time
def index():
    time.sleep(1)
    print('from index')
index()

三层语法糖(难点)

实例如下:

代码语言:javascript复制
# 判断七句print执行顺序
def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

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

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


@outter1
@outter2
@outter3   # 
def index():
    print('from index')
    
# 结果
加载了outter3   
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index

  • 自己总结了一句话,在多层语法糖中,想要知道运行的顺序,一句话概括,外层局部函数由下向上执行,内层闭包函数由上向下执行,最后执行被装饰函数中的代码!~(个人理解不喜勿喷)
  • 装饰器由下往上,遇到最后一个语法糖才会使用与函数名相同的变量名(被装饰函数)

10、装饰器修复技术

  • 定义:装饰器修复技术,为了更好的掩藏被装饰对象更不被容易被察觉出用了装饰器。将返回的装饰器id换成真正函数id地址!
  • 格式
    • from functools import wraps
    • @wraps(函数名)

实例如下:

代码语言:javascript复制
import time
from functools import wraps
# 现在可以计算有参、无参、有返回值的函数执行时间
def get_func_name(func_name):   # func_name = non_argumengt
    @wraps(func_name)
    def get_time(*args,**kwargs):
        start_time = time.time()
        # 用get_return 接收被装饰函数的返回值
        get_return = func_name(*args,**kwargs)
        end_time = time.time()
        print('函数运行时间为:',end_time-start_time)
        # 返回被装饰函数的返回值
        return get_return
    return get_time

# 定义一个无参函数
@get_func_name
def non_argument():
    time.sleep(1)
    print('执行的是无参函数')
    return 'from non_argument'
# 定义一个有参函数

@get_func_name
def have_argument(*args,**kwargs):
    time.sleep(1)
    print('执行的是有参函数,参数为:',*args,**kwargs)
    return 'from have_argument'

non_argument()
have_argument()

'''如果不写修复技术,调用装饰器打印的id地址为get_time()的id地址,写了之后查看id为被装饰函数的id地址'''

11、有参装饰器

实例如下:

代码语言:javascript复制
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')
@outer('MySQL')
def home():
    print('from home')

index()
home()
持续更新中····

0 人点赞