Bootstrap

python装饰器函数

python装饰器函数

  1. 装饰器函数是 Python 中一种特殊的函数,它可以修改或增强其他函数或类的功能,而不需要修改它们的源代码

    一个简单的例子,在register.py中写下一个装饰器函数:

    def test(original_function):
        def wrapper(*args, **kwargs):
            print("This is a wrapper function")
            result = original_function(*args, **kwargs)
            print("This is after the wrapper function")
            return result
        return wrapper
    

    新建一个usage.py,导入这个方法后增强某一函数功能,如下:

    from register import test
    
    @test
    def register(username, password):
        print(f"Registering user {username} with password {password}")
    
    if __name__ == "__main__":
        register("testuser", "testpassword")
    

    没错,控制台打印出来的结果如下:

    This is a wrapper function
    Registering user testuser with password testpassword
    This is after the wrapper function
    

    结论:虽然函数的函数体只定义了print(f"Registering user {username} with password {password}"),实际上却不仅如此。

    由此可以总结,多余的打印信息是对当前文件透明的,可以用作日志信息或其他,接着往下

  2. 打印日志

    可以添加日志信息,打印日志过程对函数透明,如下:

    def logged_funct(function):
        def wrapper(*args, **kwargs):
            print(f"Logging {function.__name__} with args {args} and kwargs {kwargs}")
            result = function(*args, **kwargs)
            print(f"Finished logging {function.__name__}")
            return result
        return wrapper
    

    装饰一个简单函数:

    @logged_funct
    def add(a, b):
        return a + b
    

    控制台输出:

    Logging add with args (1, 2) and kwargs {}
    Finished logging add
    

    *args用于将函数调用时传入的多个位置参数(非关键字参数)打包成一个元组(tuple)

    调用func(1, 2, 3, 4)时,args会是一个元组(1, 2, 3, 4)

    **kwargs用于将函数调用时传入的多个关键字参数打包成一个字典(dict)

    调用func(a=1, b=2, c=3)时,kwargs会是一个字典{'a': 1, 'b': 2, 'c': 3}

    当然,这只是简单的示例,可以根据需求扩展。

  3. 计算函数执行时间、参数检测等

    • 函数执行时间

      def time_compute(function):
          def wrapper(*args, **kwargs):
              start_time = time.time()
              function(*args, **kwargs)
              end_time = time.time()
              print(f"Time taken: {end_time - start_time} seconds")
              return end_time - start_time
          return wrapper
      
  4. 机器学习中注册数据集:

    def register_dataset(fn):
        data_name = fn.__name__
        if data_name not in _dataset_dict:
            _dataset_dict[fn.__name__] = fn
        else:
            raise ValueError(f"{data_name} already exists in dataset_dict")
        return fn
    
    
    @register_dataset
    class CustomDataset(datasets.VisionDataset):
        # 类的实现代码
    
  5. @property装饰器

    当我们不使用装饰器时,访问和修改受保护的成员时:

    class ExampleClass:
        def __init__(self):
            self._value = 0 #单下划线_:用于表示受保护的成员,是一种约定,提示程序员不要在类外部直接访问,但Python不会强制限制访问。  双下划线__:用于表示私有成员,Python会进行名称改写,以避免在子类中被意外覆盖,从而实现更严格的封装。
    
        def get_value(self):
            return self._value
    
        def set_value(self, new_value):
            if new_value >= 0:
                self._value = new_value
            else:
                raise ValueError("Value must be non-negative")
                
    obj = ExampleClass()
    print(obj.get_value())  # 获取属性值
    obj.set_value(5)  # 设置属性值
    print(obj.get_value())
    # obj.set_value(-1)  # 会引发 ValueError   
    

    十分繁琐,使用修饰器后:

    class ExampleClass:
        def __init__(self):
            self._value = 0
    
        @property
        def value(self):
            return self._value
    
        @value.setter
        def value(self, new_value):
            if new_value >= 0:
                self._value = new_value
            else:
                raise ValueError("Value must be non-negative")
    
    # 使用示例
    obj = ExampleClass()
    print(obj.value)  # 作为属性访问
    obj.value = 5  # 作为属性设置
    print(obj.value)
    # obj.value = -1  # 会引发 ValueError
    

    逻辑清晰,像是访问属性一样,实际上是调用函数。

    可以在 @property 装饰的方法中添加更多的逻辑,如计算属性值、缓存结果等,而不影响外部代码的调用方式。

    当然还可以删除属性:

        @content.deleter
        def content(self):
            print("Deleting content")
            self._content = None
            
    del file.content     
    
;