qinkwl

覃空万里

导航

装饰器

闭包

就是将数据封装在一个包(区域)中,使用时再去里面取。本质上,闭包是基于函数嵌套搞出来的一种特殊嵌套

  • 闭包的应用场景,1:封装数据防止污染全局

装饰器

import time
import functools


def func(a, b):
    print("执行函数func....")
    print(f"正在计算{a} + {b} ......")
    res = a + b
    time.sleep(3)
    print(f"函数执行结束,结果是:{a+b}")


# 当我们要计算func执行时间时,就可以通过不改变fun内部代码,通过装饰器实现
def cal_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        print(f"开始时间:{start}")
        res = func(*args, **kwargs)
        end = time.time()
        print(f"结束时间:{start}")
        print(f"总花费时间:{end-start}")
        return res
    return wrapper


@cal_time
def func_1(a, b, c):
    print("执行函数func....")
    print(f"正在计算{a} + {b} + {c}......")
    time.sleep(3)
    print(f"函数执行结束,结果是:{a+b+c}")
    return a + b + c


if __name__ == '__main__':
    v_1 = func_1(1, 2, 3)
    print(v_1)

    print(func_1.__name__)

开放封闭原则

  • 对扩展是开放的
    任何一个程序在设计之初,都不可能做到面面俱到、实现所有的功能,并且后续不作任何更改。所以,我们要支持代码扩展,添加新的功能。
  • 对修改是封闭的
    既然说要允许代码扩展,那为什么又要对修改封闭么?举个例子,比如要给银行的交易功能添加或测试新的功能,如果直接修改源代码,那么造成有些交易发生异常,甚至无法交易。所以,对修改(源码)是封闭的。
  • 装饰器的出现完美的遵循了这个开放封闭原则。

无参装饰器

装饰器的本质:装饰器本身是任意可调用对象,被装饰的对象也可以是任何可调用的对象。

装饰器的功能:在不修改被装饰对象源代码,以及调用方式的前提下,为其添加新的功能。

装饰器一般写法

def timer(func):    		# 装饰器  
    def wrapper():  
        res = func()  		# 执行被装饰函数代码  
        print(res) 			# 其他的逻辑代码
        # returen res   	# 将结果返回  
    return wrapper  		# 返回功能函数名  
@timer 					 	# 采用 @ + 装饰器名,写在被装饰函数上面,完成装饰  
def index():				# 被装饰器函数  
    print('index function') # 逻辑代码  
index()    				 	# 执行index函数,自动触发装饰器函数的执行 

第7代码的意思是拿到位于下方函数的函数名当成timer函数的参数,并执行timer函数,得到timer函数的返回值赋值给变量index (index = timer(index))

import time  
def timer(func):  
    ''' prints function time '''  
    def wrapper():  
        start = time.time()  
        func()  
        print('function %s run time %s' % (func.__name__, time.time() - start))  
    return wrapper  
@timer  						# foo = timer(foo)
def foo():  
    time.sleep(1)  
foo()

上面的代码流程是:

第1步:首先在第1行导入time模块。然后程序往下走。
第2步:在第2行定义timer函数。
第3步:接着执行到第9行,发现装饰器的语法糖。这一步你可以想象语法糖默默的帮我们做了定义foo函数,并且执行foo = timer(foo)这一步赋值操作。
第4步:然后触发timer函数执行,执行timer内部代码,程序执行第4行,定义wrapper函数。只是定义,所以程序往下走。
第5步:接着在第8行将wrapper函数名返回。
第6步:timer函数暂时执行完毕,期间做了包括将执行timer函数并为func形参传递实参foo函数名,在嵌套作用域内"记住"foo变量(foo函数名),经过这一些列操作。程序从新回到语法糖的第9行并继续往下走。
第7步:因为在第3步骤时,语法糖帮我们定义了foo函数,此时就直接执行到12行,执行foo加括号。关键点:此时的foo变量已经不是最初的foo函数那个函数名了,而是在第3步骤中拿到的foo变量,而这个foo变量实为timer函数的返回值——wrapper函数名。加括号执行的是wrapper函数。所以,此时程序执行第4行的wrapper函数,接着执行内部代码。
第8步:程序执行到第5行,通过time模块获取到当前的时间戳,程序往下走。
第9步:执行到了第6行,在局部作用域去找变量func,没找到,往上去嵌套作用域里找,这次找到了,在第3步骤中,语法糖给func传递了foo函数名,此时func加括号等价于foo函数加括号执行。
第10步:程序再次通过第9行的语法糖开始执行到第11行的foo函数的内部代码,“睡”1秒,至此,被装饰函数的内部代码执行完毕。程序往下走。
第11步:第6行的func加括号执行完毕,继续执行到了第7行的打印,通过__name__拿到了func的函数名为foo,再一次获取当前的时间戳并减去第5行时获得的时间戳,算出程序运行了多少时间,通过占位符格式化完毕,打印出结果。wrapper函数执行完毕,回到第12行。foo加括号执行完毕,继续往下走,没有代码,程序结束。

posted on 2024-12-22 20:07  覃空万里  阅读(11)  评论(0)    收藏  举报