闭包
1、在外层函数中嵌套定义了一个内层函数
2、外层函数的返回值,是内层嵌套函数的函数名,不带()
3、内层函数对外层有非全局变量的引用
def func(a): print('start', a) b = 100 def wrapper(): # 内层函数,在外层函数return时使用,内部手动调用意义不大 print('wrapper', a, b) # 内层函数有对外层函数非全局变量的引用,包括外层函数内部定义的变量或传参 print('end') return wrapper # 切记此处返回的是wrapper不带括号,带括号就是自动调用内部函数了 func(99) # 直接调用打印的是start 99和end,没有触发调用内层函数 print(func(99)) # <function func.<locals>.wrapper at 0x0000022C9A9DA430>,func(99)就是wrapper函数 res = func(99) print(res()) # res()即为wrapper(),wrapper没有返回值,所以打印None;也可以写为func(99)()
装饰器
# 引入装饰器 def work(): print('work') def fun(aa): def func1(): print(aa) aa() return func1 fun(work) # 把work函数传给fun函数并调用func()函数,传的是work不是work() print(fun(work)) # <function fun.<locals>.func1 at 0x0000024FE921A700>,内层函数的地址 res1 = fun(work) res1() # 以下两行为res1()时打印的结果 # <function work at 0x0000024FE921A5E0> # work # 以下代码块效果等同于上方引入装饰器代码块 def fun_1(aa): def func1_1(): print(aa) aa() return func1_1 # @fun_1释义:调用fun_1函数并把下方的work_1函数当做参数传进去,即为fun_1(work_1) # 然后把fun_1(work_1)的返回值赋值给了work_1,使用work_1()就是调用函数 @fun_1 def work_1(): print('work') print(work_1) # <function fun_1.<locals>.func1_1 at 0x000002526F84A700>,内层函数的地址 print(work_1()) # <function work_1 at 0x000001CDB4D0A670> work_1函数的内存地址 # work # None work_1函数没返回值,所以打印None
装饰器的作用
在不修改原功能函数内部代码的基础上,可以给原功能代码扩展新的功能
装饰器的开放封闭原则(面向对象原则的核心)
软件实体应该是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
装饰器的实现:
1、通过闭包实现装饰器
2、通过类实现装饰器(通过__call__方法实现)
# -----闭包实现装饰器 def rerun(func): # func为接收被装饰的函数 def wrapper(*args, **kwargs): for i in range(4): # 函数重运行,最多重运行3次 try: res = func(*args, **kwargs) except AssertionError as e: print(i) if i == 3: raise e else: print('通过') return res return wrapper @rerun # aa = rerun(aa) 装饰器原理:调用rerun并把aa函数当做参数传入,把结果赋值给aa def aa(): a = input('请输入:') assert a == '1' print(aa) # <function rerun.<locals>.wrapper at 0x00000157987FA550> 闭包的内层函数 # aa() # 调用 # -----通过装饰器实现函数运行时间的计算,并给出一个指定值,运行时间超过这个值的函数就打印出来 import time # 三层函数,装饰器传参、最内层函数传参 def num(number: int = 2): # 装饰器需要传参,默认值为2 def time_(func): def wrapper(*args, **kwargs): # 使用不定长参数接收被装饰的函数的传参 s_time = time.time() res = func(*args, **kwargs) e_time = time.time() if e_time - s_time > number: print(func, e_time - s_time) return res return wrapper return time_ @num(2) # 装饰器需要传参时,无论是否有默认值,都要带上后面的();2传给第一层函数 def tes_aa(a, b): # tes_aa = num(2)(test1_aa)(11, 22) for i in range(3): time.sleep(1) return a + b print(tes_aa(11, 22)) # 33 # -----编写装饰器,为多个函数加上登录认证的功能,校验登录状态 def login(): login_status['token'] = True print('登录账号密码函数的内部逻辑') login_status = {'token': False} # 使用全局变量字典格式保存登录状态 def login_stake(func): def wrapper(*args, **kwargs): if not login_status['token']: login() return func(*args, **kwargs) return wrapper # -----通过装饰器去装饰类或者有返回值的函数,装饰器内层函数一定要将类或者函数的调用结果返回 def zhuangshiqi(func): def wrapper(*args, **kwargs): print('装饰之前') res = func(*args, **kwargs) print('装饰之后') return res return wrapper @zhuangshiqi # MyTe = zhuangshiqi(MyTe) class MyTe: def yy(self): print('被装饰的类的内部函数') myt = MyTe() # 如果装饰器内层函数没有将类调用结果返回,打印的是None,这样无法创建对象,有返回值时myt是类创建的一个对象 # 此时myt这个对象不能加()调用,如需myt()调用,需使用__call__函数 print(myt) # <__main__.MyTe object at 0x00000242C231CD30> myt.yy() # 被装饰的类的内部函数 print(myt.yy()) # 被装饰的类的内部函数 None # -----非闭包实现的装饰器去装饰类 def yyy_01(self): # 此函数会被装饰为某个类的内部函数,所以需要一个self参数 print('yyy') def myddt(cls): setattr(cls, 'yyy1', yyy_01) setattr(cls, 'yyy2', 'daxigua') return cls @myddt class MyDdt: def work(self): print('work') mydt = MyDdt() print(mydt) # <__main__.MyDdt object at 0x000001BC7A81CBE0> print(mydt.yyy2) # daxigua mydt.yyy1() # yyy
# -----内置函数callable()判断一个对象是否可调用,可调用就返回True,否则就返回False # 此处的调用是指 该对象是否可以加()去调用 def work1(): pass work2 = [11,22] print(callable(work1)) # True print(callable(work2)) # False # work2() # TypeError: 'list' object is not callable 直接这样调用会报错 # 只要类里面实现了__call__方法,那么该类创建的对象就能像函数一样在后面加()去调用 class MyTest: def __call__(self, *args, **kwargs): pass mytest = MyTest() print(mytest()) # 此时__call__没有写返回值所以为None,也可以往__call__里传参 # 通过类去实现装饰器 class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print('调用之前扩展功能') res = self.func() # 调用传进来的原功能函数 print('调用之后扩展功能') return res @MyDecorator # work3 = MyDecorator(work3) def work3(): print('原函数') print(work3) # <__main__.MyDecorator object at 0x0000026964EDD970> MyDecorator类创建的对象 work3() # 调用对象,实际是去调用的__call__方法
同时使用多个装饰器时,执行装饰器顺序为从上到下执行