一文搞懂python中常用的装饰器(@classmethod、@property、@staticmethod、@abstractmethod......)

本文详细介绍了Python中的几种常见装饰器(classmethod、property、staticmethod和abstractmethod),以及如何自定义装饰器,包括类装饰器的使用示例。涵盖了装饰器的基本用法、应用场景以及注意事项。
摘要由CSDN通过智能技术生成

在这里插入图片描述

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

常见的几个装饰器介绍及示例

@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

类装饰器

非常好用的自定义装饰器

请参考此处

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值