装饰器

装饰器

一、什么是装饰器

  • 通俗的讲就是用来装饰对象的工具,这里被装饰的对象和用来装饰的装饰器都可以可以是任意的课调用的对象
  • 概括的来讲,装饰器就是在不修改被装饰对象的源代码和调用方式的基础下为被装饰对象添加额外的功能

二、装饰器的用途

  • 装饰器在装饰时,应避免修改被装饰对象的源代码和调用方式,否则一但出错,就会产生连锁反应,导致程序崩溃

三、装饰器的分类

  • 装饰器有无参装饰器和有参装饰器,但二者的原理都是一样的,都是”嵌套函数+闭包+函数对象“的组合产物

四、无参装饰器

  • 以添加计算程序时间的装饰器为例
  • 我们将以下装饰对象添加这个功能
import time


def watch():
    time.sleep(1) #为了更清楚的统计时间,让程序停顿一秒
    print("电影看完了!")"
  • 如果不使用装饰器的情况下加上计时功能:
import time


def watch():
    time.sleep(1)
    print("电影看完了!")


start_time = time.time()
watch()
end_time = time.time()
print(f"函数运行经过了{end_time - start_time}秒!")  # 函数运行经过了1.004945993423462秒!
  • 如果我们每次要计算时间时都要重复输入这几行代码会变的很繁琐,所以我们将这个功能同和成一个功能
def _time_(func):
    start_time = time.time()
    func()
    end_time = time.time()
    print(f"函数运行经过了{end_time - start_time}秒!")


_time_(watch)  # 函数运行经过了1.0062942504882812秒!
  • 这样就会改变我们调用指定函数时的调用方式,我们要在使用函数对象时在外面套上一层,再尝试修改
def _time_(func):
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f"函数运行经过了{end_time - start_time}秒!")

    return inner


watch = _time_(watch)
watch() 
'''
    电影看完了!
    函数运行经过了1.009092092514038秒!
'''
  • 但当要装饰的函数带有参数时就会报错,所以就有了有参版本的
def _time_(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print(f"程序运行所用时间{end_time - start_time}")
        return res

    return inner


def watch(name, **kwargs):
    time.sleep(2)
    return f"{name}看了电影!"


watch = _time_(watch)
print(watch("Xanadu"))
'''
    程序运行所用时间2.0120654106140137
    Xanadu看了电影
'''
  • 最终我们经过函数的嵌套完成了在不改变函数调用方式和源代码的前提下为函数增加了计算运行所用时间的功能
  • 无参装饰器模板
def outer(func):
    def inner():
        '''这里写调用 func 函数之前的逻辑'''
        res = func()
        '''这里写调用 func 函数之后的逻辑'''
        return res

    return inner


#当被装饰函数有参数时的版本
def wrapper(func):
    def inner(*args, **kwargs):
        # 第一部分:执行外部传入的函数之前执行的代码
        '''...'''
        # 第二部分:执行外部传入的函数地址(res接受外部函数的返回值,如果有返回值的情况下)
        result = func(*args, **kwargs)
        # 第三部分:执行外部传入的函数之后执行的代码
        '''...'''
        return result
    return inner

五、有参装饰器

  • 有参装饰器就是在使用装饰器装饰被装饰对象时会使用传入的参数
def decora(tag):
    if tag == 'time':
        def _time_(func):
            def inner(*args, **kwargs):
                start_time = time.time()
                res = func(*args, **kwargs)
                end_time = time.time()
                print(f"程序运行所用时间{end_time - start_time}")
                return res

            return inner

        return _time_
    if tag == 'end_time':
        def _end_time_(func):
            def outer(*args, **kwargs):
                res = func(*args, **kwargs)
                end_time = time.time()
                print(f"程序运行的结束时间是{end_time}!")

                return res

            return outer

        return _end_time_


    
# 当参数为“end_time”时就会执行装饰器中的打印结束时间的代码
@decora("end_time")
def watch(name, **kwargs):
    time.sleep(2)
    return f"{name}看了电影!"


print(watch("Xanadu"))

'''
    程序运行的结束时间是1702387268.299577!
    Xanadu看了电影!
'''

# 当参数为“time”时,就会执行装饰器中打印运行时间的代码
@decora("time")
def watch(name, **kwargs):
    time.sleep(2)
    return f"{name}看了电影!"


print(watch("Xanadu"))

"""
    程序运行所用时间2.0080788135528564
    Xanadu看了电影!
"""
  • 有参装饰器的模板
def outer(tag):
    #根据输入的tag来判断使用哪个装饰器
    if tag == "xxx":
        def wrapper_1(func):
            def inner():
                res = func
                return res

            return inner

        return wrapper_1
    elif tag == "yyy":
        def wrapper_2(func):
            def inner():
                res = func
                return res

            return inner

        return wrapper_2

六、语法糖

[1]无参语法糖

  • 此时我们就可以用timer来装饰带参数或不带参数的函数了

    • 但是为了简洁而优雅地使用装饰器,Python提供了专门的装饰器语法来取代index=timer(index)的形式
    • 需要在被装饰对象的正上方单独一行添加 @_timer_
    • 当解释器解释到 @_time_ 时就会调用timer函数
    • 且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名
    import time
    
    
    def _time_(func):
        def inner(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            end_time = time.time()
            print(f"程序运行所用时间{end_time - start_time}")
            return res
    
        return inner
    
    
    @_time_          # 直接使用@_time_来调用_time_装饰器
    def watch(name, **kwargs):
        time.sleep(2)
        return f"{name}看了电影!"
    
    
    print(watch("Xanadu")) 
    
    '''
        程序运行所用时间2.014646291732788
        Xanadu看了电影!
    '''
    

[2]有参语法糖

def decora(tag):
    if tag == 'time':
        def _time_(func):
            def inner(*args, **kwargs):
                start_time = time.time()
                res = func(*args, **kwargs)
                end_time = time.time()
                print(f"程序运行所用时间{end_time - start_time}")
                return res

            return inner

        return _time_
    if tag == 'end_time':
        def _end_time_(func):
            def outer(*args, **kwargs):
                res = func(*args, **kwargs)
                end_time = time.time()
                print(f"程序运行的结束时间是{end_time}!")

                return res

            return outer

        return _end_time_


    
# 当参数为“end_time”时就会执行装饰器中的打印结束时间的代码
@decora("end_time")
def watch(name, **kwargs):
    time.sleep(2)
    return f"{name}看了电影!"


print(watch("Xanadu"))

'''
    程序运行的结束时间是1702387268.299577!
    Xanadu看了电影!
'''

# 当参数为“time”时,就会执行装饰器中打印运行时间的代码
@decora("time")
def watch(name, **kwargs):
    time.sleep(2)
    return f"{name}看了电影!"


print(watch("Xanadu"))

"""
    程序运行所用时间2.0080788135528564
    Xanadu看了电影!
"""
posted @ 2023-12-13 21:40  桃源氏  阅读(53)  评论(0)    收藏  举报