python 装饰器
一、函数、类的数据类型
二、装饰器
一、函数、类的数据类型
下面的例子,函数、类也是一种数据类型
from types import MethodType,FunctionType class A: def func(self): pass def fun_test(x): return x if __name__ == '__main__': a = A() print(type(a.func)) # <class 'method'> 方法类型 print(type(fun_test)) # <class 'function'> 函数类型 print(isinstance(a.func, MethodType)) # True print(isinstance(fun_test, FunctionType)) # True
二、装饰器
1.什么是装饰器?
装饰器本身也是一个函数或者类,可以新增一些被装饰函数/类的功能,简化代码。
2.装饰器的分类:函数装饰器 、类装饰器---------(被装饰的对象可以是函数,也可以是类)
注意:装饰器来装饰函数或者类,被装饰的函数或类的原功能是不变的,会增加一些新的功能;
其次,被装饰的函数或者类的调用方式是不会变的
3.装饰器的语法糖
被装饰的函数/类名 = 装饰器名(被装饰的函数/类名)
4.为什么要写装饰器?
首先来看一个例子:输出结果上添加(装饰)了<>符号
def add(a,b): return a + b if __name__ == '__main__': print(f"<{add(3,4)}>") # <7>
那么,如何能直接调用函数就加上<>符号,不需要手动添加呢?就是使用装饰器,对原来的函数进行装饰。
原始装饰器的实现原理:原函数被新函数调用
def add(a,b): return a + b # 原始的装饰器 def new_add(func,*args,**kwargs): return f"<{func(*args,**kwargs)}>" if __name__ == '__main__': print(f"<{add(3,4)}>") # <7> print(new_add(add,3,4)) # <7>
原始装饰器的缺点:不够简单,需要手动传入参数
下面写个更加简单优雅的装饰器实现原理:
from types import FunctionType # 原函数 def text(): return "Hello Python!" def add_fun(func:FunctionType): def wrapper(): return f"<i>{func()}</i>" return wrapper if __name__ == '__main__': print(add_fun(text)()) # <i>Hello Python!</i>
加注解符号@,直接调用原函数,就可以实现对原函数的装饰。
from types import FunctionType def add_fun(func:FunctionType): def wrapper(): return f"<i>{func()}</i>" return wrapper @add_fun def text(): return "Hello Python!" if __name__ == '__main__':
#直接调用原函数 print(text()) # <i>Hello Python!</i>
装饰器的意义:团队合作开发中,一段代码被多人调用,又要增加不同的新需求,每个人都直接在原函数上作改动,是不现实的。用装饰器,既不影响原函数的调用(不修改原函数),又可以在原函数的基础增加新的功能,更加合适。
5.一个函数被多个装饰器 装饰
原理:从下往上
执行:从上往下
from types import FunctionType def add_fun(func: FunctionType): def wrapper(): return f"<i>{func()}</i>" return wrapper def add_bold(func): def wrapper2(): return f"<b>{func}</b>" return wrapper2 @add_fun # wrapper2() = add_fun() = wrapper() @add_bold # text() = add_bold(text)()=wrapper2() def text(): return "Hello Python!" if __name__ == '__main__': print(text()) # <i><b><function text at 0x10e0cfb00></b></i>
6.装饰器 的应用
1)函数装饰器---用来装饰函数
①实战1
# 需求:写一个装饰器,能对任意函数进行装饰,功能:记录日志 def log(func): def wrapper(*args, **kwargs): print(f"{func.__name__}函数正在运行...") return func(*args, **kwargs) return wrapper @log def add(a, b): return a + b if __name__ == '__main__': print(add(1,2)) """ add函数正在运行... 3 """
②计算函数的运行时间
# 计算函数运行的时间 import time def caculate_time(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) time.sleep(0.5) end_time = time.time() return end_time - start_time return wrapper @caculate_time def add(a, b): return a + b if __name__ == '__main__': print(add(3, 4)) # 0.5043981075286865
2)类的装饰器---用来装饰类
# 类的装饰器 """ 格式 def 装饰器名(cls): 装饰代码 return cls """ name = "python" def add_name(cls): cls.name = name return cls @add_name class A(object): pass if __name__ == '__main__': a = A() print(a.name)
3)装饰器类
_ _call_ _方法:函数的调用,实际就是调用函数里的_ _call_ _方法
# 这个装饰器是一个类(不是函数) """ 格式 class 装饰器名(object): def __init__(self,func): pass def __call__(self,*args,**kwargs): # 调用类的对象时,call会被自动调用执行 装饰代码段 return self.func(*args,**kwargs) """ class A(object): def __init__(self,func): self.func = func def __call__(self, *args, **kwargs): print(f"{self.func.__name__}函数正在被调用...") result = self.func(*args, **kwargs) return result @A def add(a, b): return a + b if __name__ == '__main__': print(add(1,2))
4)带参数的装饰器
# 带参数的装饰器-三层函数(最外层用来传参)---装饰器里面可以传参数 # @log(filename = "123.txt") import logging def add_log(filename): # 最外层:接收装饰参数 def inner(func): # 中间层:接收被装饰函数 def wrapper(*args, **kwargs): # 内层装饰函数:接收被装饰的函数的参数 logging.warning(f"{func.__name__}函数正在被调用...") result = func(*args, **kwargs) # 调用被装饰函数 return result return wrapper return inner @add_log(filename="123.txt") def add(a, b): return a + b if __name__ == '__main__': print(add(3,4)) """ WARNING:root:add函数正在被调用... 7 """