python闭包与装饰器

169 阅读2分钟

思路源自于 BV1SZ4y1s7cv

闭包与装饰器

假如要实现一个功能,包含了两个功能,一个是计算运行的时间,一个是计算奇数,那么可以这样实现:

def index():
    start_time = time.time()
    for i in range(10000):
        if i % 2 == 1:
            print(i)
    end_time = time.time()
    timex = end_time - start_time
    print("it takes " + str(timex))

if __name__ == "__main__":
    index()

那么这两个功能就会变得耦合,那么就要将其分离为两个函数,应使用闭包:

def count_time_wrapper(func):
    # 闭包,用于增强函数func,给函数func增加统计时间的功能
    def improved_func(*args,**kwargs):
        start_time = time.time() # 当前时间
        odd = func(*args,**kwargs)
        end_time = time.time()# 当前时间
        print("it takes " + str(end_time-start_time))  # 执行func消耗的时间
        return odd
    return improved_func

def print_odds(lim = 1000):
    sum = 0
    for i in range(lim):
        if i%2 == 1:
            sum += 1
    return sum
    
if __name__ == "__main__":
    print(count_time_wrapper(print_odds).__name__) # improved_odds
    print(print_odds =count_time_wrapper(print_odds)(lim = 10000))
    """
    最终运行结果为:
        improved_func
        it takes 0.0009989738464355469
        5000
    """

其中count_time_wrapper(print_odds)得到的是improved_func函数,并且已经传入了print_odds函数作为参数,后面加上去(lim = 10000)表示执行了improved_func(print_odds)。这样做的好处在于两个功能分为了两个函数。

假如在print_odds函数上面加上装饰器,如下:

def count_time_wrapper(func):
    # 闭包,用于增强函数func,给函数func增加统计时间的功能
    def improved_func(*args,**kwargs):
        start_time = time.time() # 当前时间
        odd = func(*args,**kwargs)
        end_time = time.time()# 当前时间
        print("it takes " + str(end_time-start_time))  # 执行func消耗的时间
        return odd
    return improved_func

@count_time_wrapper # 装饰器
def print_odds(lim = 1000):
    sum = 0
    for i in range(lim):
        if i%2 == 1:
            sum += 1
    return sum
    
if __name__ == "__main__":
    """
    print_odds 上了装饰 器 @count_time_wrapper 后,等价于在第一次调用函数时执行了以下语句:
    print_odds =count_time_wrapper(print_odds)  
    """
    print(count_time_wrapper(print_odds).__name__)
    print(print_odds(lim = 10000))
    """
    最终运行结果为:
        improved_func
        it takes 0.0009989738464355469
        5000
    """

也就是说加上去装饰器后第一次执行print_odds(lim = 10000)相当于执行了print_odds =count_time_wrapper(print_odds) ,相当于是增强了print_odds函数的功能,后面再使用print_odds(lim = 10000)时就不会在增强了,也就是说只增强一次。

多重装饰器

def wrapper1(fun):
    print("this is 1")
    def callback():
        print("this is 1 callback")
        fun()
    return callback

def wrapper2(fun):
    print("this is 2")
    def callback():
        print("this is 2 callback")
        fun()
    return callback

@wrapper1
@wrapper2
def wrapper3():
    print("this is 3")

if __name__ == "__main__":
    # 第一次执行了wrapper3 = wrapper1(wrapper2(wrapper3))
    wrapper3()
    print("----")
    # 后面就不再进行增强,也就是不执行wrapper3 = wrapper1(wrapper2(wrapper3)),因为第一次已经执行过了
    wrapper3()
    
    """
    结果:
        this is 2
        this is 1
        this is 1 callback
        this is 2 callback
        this is 3
        ----
        this is 1 callback
        this is 2 callback
        this is 3
    """