Loading

装饰器

【一】什么是装饰器

  • 器:工具
  • 装饰:为其他事物添加额外的功能
  • 定义一个函数,这个函数的功能就是用来装饰其他函数的
  • 也就是说这个函数就是用来给其他函数添加额外功能用的

提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。

【二】开放封闭原则

  • 开放:对拓展功能(增加功能)开放,意思是再源代码不做任何改变的情况下,为其增加功能
  • 封闭:对修改源代码是封闭的
def inside(x,y):
    print(x)
    print(y)
  • 装饰器:在不修改被装饰对象的源代码,也不修改调用方式的前提下,给装饰对象添加新的功能

【三】代码

【1】引入

  • 用王者荣耀的开局语言模拟来引入
import time


def inside(group, s):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print('全军出击')


inside('红', 5)
"""
欢迎来到王者荣耀
你出生在红方阵营
敌军还有5秒到达战场
全军出击
"""

【2】需求

  • 需要统计程序运行的时间

(1)方案一

import time


def inside(group, s):
    start = time.time()
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print('全军出击')
    end = time.time()
    print(end-start)

inside('红', 2)

"""
欢迎来到王者荣耀
你出生在红方阵营
敌军还有2秒到达战场
全军出击
2.000739574432373
"""
  • 违反的封闭原则,没有修改函数的调用方式,但是修改了函数源代码,所以这个方案舍弃掉

(2)方案二

def inside(group, s):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print('全军出击')


start = time.time()
inside('红', 2)
end = time.time()
print(end - start)
"""
欢迎来到王者荣耀
你出生在红方阵营
敌军还有2秒到达战场
全军出击
2.000739574432373
"""
  • 这种方法没有修改源代码也没有修改函数的调用方式
  • 但是如果我们有100个函数都需要增加这个计算运行时间的功能
  • 就会出现很多重复的代码,这是一种十分low的行为
  • 所以这个方案也舍弃

(3)方案三

def inside(group, s):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print('全军出击')


def wrapper():
    start = time.time()
    inside('红', 2)
    end = time.time()
    print(end - start)

wrapper()
  • 解决的方案二的代码冗余问题,也没有修改被装饰对象的源代码,同时还为其增加了新的功能
  • 但是被装饰对象的调用方式被修改了
  • 所以这个方案也得舍弃

(4)方案四

  • 暂且先不考虑如何解决不修改调用方式的问题
  • 看方案三的代码可以看到这个函数是一个死的函数
  • 如果被装饰对象的形参发生了修改,那就没有办法 了
def inside(group, s, z):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print(f'{z}出击')


def wrapper(*args, **kwargs):
    start = time.time()
    inside(*args, **kwargs)
    end = time.time()
    print(end - start)


wrapper('红', 2, '炮车')

  • 通过*args和**kwargs,我们只需要按照被装饰对象的形参格式,随意传参就行了。
  • 可是这样一看,如果我们需要装饰其他的函数功能就无法达到效果了
  • 我们不能把被装饰对象写死,所以引出了方案五

(5)方案五

def inside(group, s, z):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print(f'{z}出击')


def wrapper(*args, **kwargs):
    start = time.time()
    func(*args, **kwargs) ##这样子写会报红,应为wrapper没有接收到一个func
    end = time.time()
    print(end - start)


wrapper('红', 2, '炮车')

  • 思考解决办法
    1. 给函数传参
      • 直接通过参数传(wrapper的参数是原封不动的传给被装饰对象函数的,需要严格按照被装饰对象的形参,所以这个方法不可行)(False)
      • 通过闭包函数传(True)
def inside(group, s, z):
    print('欢迎来到王者荣耀')
    print(f'你出生在{group}方阵营')
    print(f'敌军还有{s}秒到达战场')
    time.sleep(s)
    print(f'{z}出击')


def outer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print(end - start)
    return wrapper # 注意不能加括号,加括号就变成调用了,我们只需要wrapper的内存地址


inside = outer(inside)
inside('红',2,'跑车')
  • 这样子就没有修改被装饰对象的源代码,且没有修改调用方式
  • 而且没有太多的代码冗余
  • 就完成了给被装饰对象添加功能
posted @ 2024-01-13 23:13  HuangQiaoqi  阅读(10)  评论(0编辑  收藏  举报