python

装饰器
  • 语法糖格式含义:@log 等价于 func = log(func)@log('execute') 等价于 func = log('execute')(func)
  • Decorator 是返回函数的高级函数,可以在不改变原有函数代码的情况下为该函数增加新的功能
普通装饰器
# 计时器 def calc_func_runtime_no_wrapped_funtools(func): def wrapper(*args, **kwargs): start = time.perf_counter() res = func(*args, **kwargs) end = time.perf_counter() print(f'{func.__name__} execute in {end-start}s') return res return wrapper def calc_func_runtime(func): # 返回扩展过功能的函数 @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() res = func(*args, **kwargs) end = time.perf_counter() print(f'{func.__name__} execute in {end-start}s') return res return wrapper @calc_func_runtime_no_wrapped_funtools # 等价于now = calc_func_runtime_no_wrapped_funtools(now) def now(): print('2024.2.4') @calc_func_runtime # 等价于now = calc_func_runtime(now) def now_functols_wrapped(): print('2024.2.4') # 输出函数名 print(now.__name__) # wrapper print(now_functols_wrapped.__name__) # now_functols_wrapped
可传参数的装饰器
# 记录日志函数 # 不带参数 def log(func): def wrapper(): print("Logging execution") func() print("Finished logging") return wrapper def log_of_param(param): def decorator(func): def wrapper(): print(f"Logging with param: {param}") func() print(f"Finished logging with param: {param}") return wrapper return decorator @log # 等价于 now = log(now) @log_of_parameter('execute') # 等价于 now = log_of_parameter('execute')(now),第一步log_of_parameter('execute')返回decorator函数,第二步返回装饰后的函数 def now(): print('2024.2.4') now() # 执行结果 # Logging execution # Logging with param: execute # 2024.2.4 # Finished logging with param: execute # Finished logging
  • 如果装饰器本身需要传入参数,那么可以定义一个返回decorator的高级函数
  • 当有多个装饰器时,它们的执行顺序是从靠近被装饰对象的的开始由内向外执行;对于函数对象,运行函数时时从外向内执行,如可传参数的装饰器
  • 使用 @functools.wraps 是为了同步原函数的属性信息(比如__name__(函数名))
  • python内置装饰器
    • @staticmethod:表示这个函数是静态方法,因为某种原因放在此类中,实际上与此类毫无关系,但是可以并且只能通过此类名来调用被装饰的函数
    • @classmethod:在重构类时不需要修改构造函数(__init__),如果想增加新的方法,此时要用到父类,只需要给该方法添加@classmethod即可实现
      • 在已写好初始类的情况下,想给初始类再新添功能,不需要改初始类,只要在新类继承初始类并在内部新写一个方法,方法用@classmethod装饰一下即可。
      • 此修饰器装饰的方法不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
      # 初始类:classData_test(object): day=0 month=0 year=0 def__init__(self,year=0,month=0,day=0): self.day=day self.month=month self.year=year defout_date(self): print "year :" print self.year print "month :" print self.month print "day :" print self.day # 新增功能:classStr2IntParam(Data_test): @classmethod defget_date(cls, string_date): #这里第一个参数是cls, 表示调用当前的类名year,month,day=map(int,string_date.split('-')) date1=cls(year,month,day) #返回的是一个初始化后的类return date1 # 使用:r= Str2IntParam.get_date("2016-8-1") r.out_date() # 输出:year : 2016 month : 8 day : 1
      @property:创建只读属性,将方法转化为同名称的属性,使方法像属性一样被访问
      • 使用@property装饰器可以将一个方法转换为同名的只读属性,这意味着当你尝试访问这个属性时,实际上是在调用一个方法
      property 装饰器是 Python 中的一个内置装饰器,用于创建只读属性。它允许将类中的方法转换为看似访问属性一样的方式进行调用,这有助于实现数据封装和数据隐藏。通过使用 property 装饰器,您可以在不改变类接口的情况下,更改属性背后的实现细节。

      使用 property 装饰器

      python复制代码 class MyClass: def __init__(self): self._my_attribute = None # 通常使用单下划线前缀表示“私有”属性 @property def my_attribute(self): """这是一个只读属性""" return self._my_attribute
      在上面的例子中,my_attribute 方法被 @property 装饰器修饰,这意味着你可以像访问数据属性一样访问 my_attribute 方法:
      python复制代码 obj = MyClass() value = obj.my_attribute # 不需要括号,就像访问属性一样,调用了类中的方法
      但是,只用 @property 并不能设置属性的值。为了能够设置属性的值,还需要定义一个 setter 函数:

      添加 setter 函数

      python复制代码 class MyClass: def __init__(self): self._my_attribute = None @property def my_attribute(self): """获取属性值""" return self._my_attribute @my_attribute.setter def my_attribute(self, value): """设置属性值""" # 在这里可以添加任何验证或处理代码 self._my_attribute = value
      现在,你既可以获取也可以设置 my_attribute 的值了,并且在设置值时可以包含任何必要的逻辑,比如类型检查、验证等:
      python复制代码 obj = MyClass() obj.my_attribute = 42# 设置属性值print(obj.my_attribute)# 获取并打印属性值

      添加 deleter 函数

      如果你还想要控制删除属性的行为,可以添加一个 deleter 函数:
      python复制代码 class MyClass: def __init__(self): self._my_attribute = None @property def my_attribute(self): """获取属性值""" return self._my_attribute @my_attribute.setter def my_attribute(self, value): """设置属性值""" self._my_attribute = value @my_attribute.deleter def my_attribute(self): """删除属性""" del self._my_attribute
      使用 deleter 可以执行清理操作或者其他特定逻辑:
      python复制代码 obj = MyClass() del obj.my_attribute# 删除属性

      总结

      property 装饰器使得开发者可以在不改变类的外部调用方式的情况下,对属性的访问进行更精细的控制。通过在属性的 getter、setter 和 deleter 方法中添加逻辑,您可以确保属性值总是符合预期,并且可以方便地随着项目的变更而更新底层逻辑。
      示例
      class A(object): # 属性默认为类属性(可以给直接被类本身调用) num = "类属性" # 实例化方法(必须实例化类之后才能被调用) def func1(self): # self : 表示实例化类后的地址id print("func1") print(self) # 类方法(不需要实例化类就可以被类本身调用) @classmethod def func2(cls): # cls : 表示没用被实例化的类本身 print("func2") print(cls) print(cls.num) cls().func1() # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准,标准的做法是使用 @staticmethod 装饰) # @staticmethod def func3(): print("func3") print(A.num) # 属性是可以直接用类本身调用的 # A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的 A.func2() A.func3()
  • 类装饰器
    • 不带参数的类装饰器
      • 在类的初始化函数 _init__() 方法中定义装饰的内容
      • 使用 @Student 装饰函数等价于 now = Student(now)
      • 【注意】传入的是函数对象,返回值是一个类的实例对象
      • 示例
        class Student(): def __init__(self, func): print('__init__') func() def __call__(self): print('__call__') # 等价于 now = Student(now) @Student def now(): print('2024.02.06') print('Today is 2024.02.06') now()
    • 带参数的类装饰器
      • 在类的 __call__() 方法中定义装饰的内容
      • 使用 @Student(arg) 装饰函数等价于 now = Student(arg)(now) ,即先执行 Student(arg) 获得类的实例对象,接着调用类的实例对象并传入一个函数。
      • 【注意】返回值是一个函数对象
      • 示例
        class Decorator(): def __init__(self, arg): self.__arg = arg def __call__(self, func): print('__call__') def wrapper(*args, **kwargs): print('wrapper') result = func(*args, **kwargs) print(self.__arg) return result return wrapper # 等价于 add = Decorator('Decorator')(add) @Decorator('Decorator') def add(a, b): print('add') return a+b print('This is Main') res = add(1, 3) print(res)
缓存装饰器
import functools from datetime import datetime def redis_cache(func): @functools.wraps(func) # 常见的用法是@wraps,这是因为在引入模块functools时直接导入了wraps def wrapper(*args, **kwargs): # 如果使用了位置参数,在args中按位置访问参数 start_date = args[0] end_date = args[1] # 如果使用了关键字参数,从kwargs字典中获取start_date和end_date: start_date = kwargs.get('start_date') end_date = kwargs.get('end_date') # 这里可以根据start_date和end_date进行一些操作,比如生成缓存键等 # 执行实际的函数调用并返回结果 return func(*args, **kwargs) return wrapper @redis_cache def get_data(start_date: datetime, end_date: datetime): # 实际函数实现 pass
计时装饰器
import time from functools import wraps def calculate_runtime(func): @wraps(func) def wrapper(*args, **kwargs): start_time = time.perf_counter() result = func(*args, **kwargs) end_time = time.perf_counter() print(f'{func.__name__} execute in {end_time - start_time}s') return result return wrapper
日志记录装饰器
import functools # 简单日志装饰器 def log(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('call log') print(f'{func.__name__} start execute') result = func(*args, **kwargs) print('end log') return result return wrapper # 可带参数的日志装饰器 def log_of_parameter(arg): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'call log:{arg}') print(f'{func.__name__} is execute') result = func(*args, **kwargs) print('end log') return result return wrapper return decorator # 自适应的日志装饰器,可带参数可不带参数 def log_enhance_mode(arg): if callable(arg): @functools.wraps(arg) def wrapper(*args, **kwargs): print('call log') print(f'{arg.__name__} is execute') result = arg(*args, **kwargs) print('end log') return result return wrapper def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'call log:{arg}') print(f'{func.__name__} is execute') result = func(*args, **kwargs) print('end log') return result return wrapper return decorator
If you have any questions, please contact me.