Python中常用装饰器一览(@classmethod、@property、@staticmethod、@abstractmethod等)

本文分为两部分,第一部分是介绍python中常见的装饰器。另一部分是自定义装饰器,包括了一些非常好用的自定义装饰器。

一文搞懂python中常用的装饰器

  • 常见的几个装饰器介绍及示例
  • @classmethod 装饰器基本用法
  • @property、@setter装饰器基本用法
  • @staticmethod 装饰器基本用法
  • @abstractmethod装饰器基本用法
  • 自定义装饰器
  • 类装饰器
  • 非常好用的自定义装饰器
  • 常见的几个装饰器介绍及示例

    @classmethod 装饰器基本用法

    1. 定义及语法

    @classmethod 装饰器用于定义类方法。类方法与普通方法不同,它在类层级上操作,而不是在实例层级上。通过类方法,我们可以直接通过类名调用方法,而无需创建类的实例。并且第一个参数通常被命名为 cls,代表类本身,而不是常见的self。类方法可以访问类的属性和其他类方法(不包括实例方法),但不能直接访问实例的属性。

    以下是使用@classmethod装饰器定义类方法的语法:

    class MyClass:
        @classmethod
        def my_method(cls, arg1, arg2, ...):
            # 方法体
    
    1. 三种常用使用场景及示例

    示例1:访问类的属性搭配使用,注意不是实例的属性self.xxx。示例中统计创建了多少个对象。

    class MyClass:
        count = 0 # 类的属性 而不是实例属性
    
        def __init__(self):
            MyClass.count += 1
    
        @classmethod
        def get_count(cls):
            return cls.count
    
    obj1 = MyClass()
    obj2 = MyClass()
    obj3 = MyClass()
    print(MyClass.get_count())  # 输出:3
    

    示例2:从字符串中创建对象,类方法可以提供一种替代构造方法的方式。

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def display(self):
            print(f"Name: {self.name}, Age: {self.age}")
    
        @classmethod
        def from_string(cls, string):
            name, age = string.split(",")
            return cls(name, int(age))
    
    person_str = "John,25"
    person = Person.from_string(person_str)
    person.display()  # 输出:Name: John, Age: 25
    

    在这个例子中,我们通过from_string()方法从一个字符串中创建了一个Person对象。该方法接收一个字符串,将字符串按照分隔符创建,并返回一个新的Person对象。

    示例3:根据不同的类型参数返回不同的子类对象。

    class Shape:
        def __init__(self, color):
            self.color = color
    
        def display_color(self):
            print(f"Color: {self.color}")
    
        @classmethod
        def create_shape(cls, color, type):
            if type == "circle":
                return Circle(color)
            elif type == "rectangle":
                return Rectangle(color)
    
    class Circle(Shape):
        def display_shape(self):
            print("Type: Circle")
    
    class Rectangle(Shape):
        def display_shape(self):
            print("Type: Rectangle")
    
    circle = Shape.create_shape("red", "circle")
    circle.display_color()  # 输出:Color: red
    circle.display_shape()  # 输出:Type: Circle
    
    rectangle = Shape.create_shape("blue", "rectangle")
    rectangle.display_color()  # 输出:Color: blue
    rectangle.display_shape()  # 输出:Type: Rectangle
    

    在这个示例中,我们通过工厂模式创建了Shape的子类对象。create_shape()方法根据不同的类型参数返回不同的子类对象,达到生成不同形状的对象的目的。

    1. 注意事项:类方法中不能调用实例方法(第一个参数是self的方法)。但可以调用其他类方法或者静态方法(@staticmethod),示例如下。
    class MyClass:
        @classmethod
        def class_method(cls):
            print("This is a class method")
            cls.other_method()
            cls.other_method2()
            
        @staticmethod
        def other_method():
            print("This is another method")
            
        def instance_method(self):
            print("This is an instance method")    
        
        @classmethod
        def other_method2(cls):
            print("This is another method")
            obj = cls()
            obj.instance_method()
    
    # 调用类方法
    MyClass.class_method()
    

    @property、@setter装饰器基本用法

    1. 定义
      @property装饰器是Python中一个特殊的装饰器,用于定义类的属性。它提供了一种简洁的方式来定义属性的访问方法,并且可以在需要时进行计算或验证。应用于类的实例方法,将其转换为类的属性。通过使用@property装饰器,可以将一个方法定义为只读属性,也可以定义一个可读写的属性,并且可以实现属性删除

      @setter装饰器用于定义一个属性的setter方法,用于修改属性的值。使用@setter时,我们需要在@property装饰的属性方法之后,紧跟一 个setter方法,并使用@属性名.setter来装饰该方法

      @setter通常用于以下场景:
      当一个属性的值需要计算得到,而不是直接存储在类的属性中时,我们可以使用@setter来提供一个修改属性值的接口。
      当某个属性的值需要经过一些处理后再进行存储时,我们可以使用@setter来自动执行处理操作。

    2. 基本用法

    示例1:可读写属性、属性删除

    class Person:
        def __init__(self, name):
            self._name = name # 私有属性
        
        @property       
        def name(self):        # 读取私有属性
            return self._name
        
        @name.setter          # 可写属性 
        def name(self, value):
            self._name = value
        
        @name.deleter
        def name(self):      # 删除属性
            del self._name
    
    person = Person("Alice")
    print(person.name)  # 输出: Alice
    del person.name
    print(person.name)  # 抛出AttributeError异常,属性已被删除
    

    在上面的例子中,name属性定义了获取、设置和删除方法。通过使用@property.deleter装饰器,定义了name属性的删除方法。在删除name属性时,可以使用del语句。

    示例2:属性计算,@property 装饰器可以用于对属性的计算,使得通过访问属性时能够返回计算后的结果。

    class Rectangle:
        def __init__(self, width, height):
            self._width = width
            self._height = height
    
        @property
        def width(self):
            return self._width
    
        @width.setter
        def width(self, value):
            if value >= 0:
                self._width = value
            else:
                raise ValueError("Width must be a non-negative number.")
    
        @property
        def height(self):
            return self._height
    
        @height.setter
        def height(self, value):
            if value >= 0:
                self._height = value
            else:
                raise ValueError("Height must be a non-negative number.")
    
        @property
        def area(self):
            return self._width * self._height
    
        @property
        def perimeter(self):
            return 2 * (self._width + self._height)
    
    rectangle = Rectangle(4, 5)
    print(rectangle.width)  # Output: 4
    print(rectangle.height)  # Output: 5
    print(rectangle.area)  # Output: 20
    print(rectangle.perimeter)  # Output: 18
    
    rectangle.width = 6
    rectangle.height = 8
    print(rectangle.width)  # Output: 6
    print(rectangle.height)  # Output: 8
    print(rectangle.area)  # Output: 48
    print(rectangle.perimeter)  # Output: 28
    
    

    在上面的代码中,我们定义了一个名为 Rectangle 的类,它具有 width、height、area 和 perimeter 四个属性。面积和周长是通过 width 和 height 属性的计算得到的,通过使用 @property 装饰器,我们将这两个方法定义为属性。

    注意
    静态方法和类方法:@property装饰器只能应用于实例方法,而不能应用于静态方法或类方法。它是用于访问实例属性的装饰器。

    私有属性:私有属性是名称前面添加一个下划线,例如self._property。这样可以向其他开发人员传达属性的可见性意图,虽然在Python中并没有真正的私有属性,私有属性也可以被访问。

    调用setter:在setter方法内部,我们使用self.属性名对属性进行赋值。在调用setter方法时,我们只需要给属性赋值,不需要加圆括号。

    @staticmethod 装饰器基本用法

    1. 定义
      @staticmethod装饰器是Python中用于定义静态方法的装饰器。静态方法是与类相关联但不依赖于实例的方法。它们可以直接通过类名调用,而无需创建类的实例。

    静态方法具有以下特点:

  • 不访问实例状态:静态方法不需要访问实例的状态或属性。它们通常用于执行与类相关的功能,但与特定实例无关。

  • 通过类名调用:静态方法可以直接通过类名调用,而无需创建类的实例。这使得静态方法在不依赖于实例的情况下提供了一种方便的功能。

  • 不需要self参数:静态方法的定义不需要self参数。由于静态方法不操作实例的状态,因此不需要将实例作为参数传递。

  • 不能访问实例变量:静态方法无法访问实例变量,因为它们与实例无关。它们只能访问类级别的属性和其他静态方法。

  • 可以访问类级别的属性和方法:静态方法可以访问类级别的属性和其他静态方法。它们可以与类的其他成员进行交互,但无法直接访问实例级别的成员。

    1. 基本用法

      示例1
      静态方法通常被用于处理和类相关的计算逻辑,而与类实例的状态无关。例如,我们可以在一个日期类中定义一个静态方法,用于判断某一年是否为闰年。因为判断闰年与具体的日期实例无关,所以这个方法可以被定义为静态方法。

    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        @staticmethod
        def is_leap_year(year):
            if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
                return True
            else:
                return False
    
        def display_date(self):
            print(f"Date: {self.year}-{self.month}-{self.day}")
    
    # 创建日期实例
    my_date = Date(2022, 10, 1)
    
    # 调用静态方法判断是否为闰年
    if Date.is_leap_year(my_date.year):
        print(f"{my_date.year} is a leap year.")
    else:
        print(f"{my_date.year} is not a leap year.")
    

    静态方法不需要访问实例的状态或属性,因此不需要传递self参数。在静态方法内部,无法访问类的实例变量,因为它们与实例无关。
    可以直接使用类名调用静态方法,如Date.is_leap_year(my_date.year),而无需创建类的实例。

    示例2:类中的静态方法可以被类方法和实例方法访问

    class MyClass:
        @staticmethod
        def static_method():
            print("This is a static method.")
        
        @classmethod
        def class_method(cls):
            cls.static_method()
            print("This is a class method.")
        
        def instance_method(self):
            self.static_method()
            print("This is an instance method.")
    
    # 通过类名调用静态方法
    MyClass.static_method()
    # 输出: This is a static method.
    
    # 通过类方法调用静态方法
    MyClass.class_method()
    # 输出:
    # This is a static method.
    # This is a class method.
    
    # 创建类的实例
    obj = MyClass()
    # 通过实例调用静态方法
    obj.static_method()
    # 输出: This is a static method.
    
    # 通过实例方法调用静态方法
    obj.instance_method()
    # 输出:
    # This is a static method.
    # This is an instance method.
    

    @abstractmethod装饰器基本用法

    @abstractmethod 是一个装饰器,用于声明抽象方法。抽象方法是在基类中定义但没有具体实现的方法,它需要在派生类中进行具体的实现。

    注意事项:

  • 抽象方法的主要目的是定义一个接口,规定了派生类必须提供的方法。它在面向对象设计中非常有用,因为它可以确保派生类遵循特定的接口约定。

  • 需要注意的是,抽象方法只能在抽象基类中定义,而不能直接实例化抽象基类。因此,抽象基类本身无法被实例化,只能被用作其他类的基类。

  • 抽象基类必须继承自 ABC 基类,并使用 @abstractmethod 装饰器标记抽象方法。

  • 派生类必须实现基类中的抽象方法,否则会引发 TypeError。

    1. 基本用法

    示例:使用抽象类进行多态

    from abc import ABC, abstractmethod # 第一步:导入 abc
    
    # 第二步: 定义抽象基类
    class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    # 第三步:创建派生类并实现抽象方法
    class Cat(Animal):  # 动物的形态之一:猫
        def talk(self):
            print('This is a cat')
            
    class Dog(Animal):  # 动物的形态之二:狗
        def talk(self):
            print('This is a dog')
    
    
    c = Cat()
    d = Dog()
    
    def func(obj):
        obj.talk()  # 同一函数名,根据对象不同实现不同方法
    
    func(c)  # 'This is a cat'
    func(d)  # 'This is a dog'
    

    在上面的例子中,Animal类定义了一个抽象方法talk(),子类CatDog分别实现了这个抽象方法。可以通过抽象类类型的变量来分别引用CatDog类的对象,并调用它们的 talk() 方法。

    自定义装饰器

    # """ 装饰器:格式规范 """
    # 第一步:写装饰器函数 (使用python内置装饰器时可省略)
    
    
    def decorator_name(f):      # f为被修饰的函数
        @wraps(f)
    # @wraps()接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
        def decorated(*args, **kwargs):     # 对函数f的装饰,即添加新功能
            if not can_run:
                return "Function will not run"
            return f(*args, **kwargs)
        return decorated
    
    # 第二步:@装饰器在被装饰的函数func上方,得到被装饰后的func()
    
    
    @decorator_name     # @是一种简写,代表了func = decorator_name(func)
    def func():
        return("Function is running")
    
    
    # 此时的 func()已经是被装饰后的func()
    can_run = True
    print(func())
    # Output: Function is running
    
    can_run = False
    print(func())
    # Output: Function will not run
    

    类装饰器

    非常好用的自定义装饰器

    请参考此处

    作者:NosONE

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python中常用装饰器一览(@classmethod、@property、@staticmethod、@abstractmethod等)

    发表回复