Python中常用装饰器一览(@classmethod、@property、@staticmethod、@abstractmethod等)
本文分为两部分,第一部分是介绍python中常见的装饰器。另一部分是自定义装饰器,包括了一些非常好用的自定义装饰器。
一文搞懂python中常用的装饰器
常见的几个装饰器介绍及示例
@classmethod 装饰器基本用法
- 定义及语法
@classmethod
装饰器用于定义类方法。类方法与普通方法不同,它在类层级上操作,而不是在实例层级上。通过类方法,我们可以直接通过类名调用方法,而无需创建类的实例。并且第一个参数通常被命名为 cls
,代表类本身,而不是常见的self
。类方法可以访问类的属性和其他类方法(不包括实例方法),但不能直接访问实例的属性。
以下是使用@classmethod装饰器定义类方法的语法:
class MyClass:
@classmethod
def my_method(cls, arg1, arg2, ...):
# 方法体
- 三种常用使用场景及示例
示例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()方法根据不同的类型参数返回不同的子类对象,达到生成不同形状的对象的目的。
- 注意事项:类方法中不能调用实例方法(第一个参数是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装饰器基本用法
-
定义
@property
装饰器是Python中一个特殊的装饰器,用于定义类的属性。它提供了一种简洁的方式来定义属性的访问方法,并且可以在需要时进行计算或验证。应用于类的实例方法,将其转换为类的属性。通过使用@property装饰器,可以将一个方法定义为只读属性,也可以定义一个可读写的属性,并且可以实现属性删除。@setter
装饰器用于定义一个属性的setter方法,用于修改属性的值。使用@setter时,我们需要在@property
装饰的属性方法之后,紧跟一 个setter方法,并使用@属性名.setter
来装饰该方法@setter
通常用于以下场景:
当一个属性的值需要计算得到,而不是直接存储在类的属性中时,我们可以使用@setter来提供一个修改属性值的接口。
当某个属性的值需要经过一些处理后再进行存储时,我们可以使用@setter来自动执行处理操作。 -
基本用法
示例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 装饰器基本用法
- 定义
@staticmethod
装饰器是Python中用于定义静态方法的装饰器。静态方法是与类相关联但不依赖于实例的方法。它们可以直接通过类名调用,而无需创建类的实例。
静态方法具有以下特点:
不访问实例状态:静态方法不需要访问实例的状态或属性。它们通常用于执行与类相关的功能,但与特定实例无关。
通过类名调用:静态方法可以直接通过类名调用,而无需创建类的实例。这使得静态方法在不依赖于实例的情况下提供了一种方便的功能。
不需要self参数:静态方法的定义不需要self参数。由于静态方法不操作实例的状态,因此不需要将实例作为参数传递。
不能访问实例变量:静态方法无法访问实例变量,因为它们与实例无关。它们只能访问类级别的属性和其他静态方法。
可以访问类级别的属性和方法:静态方法可以访问类级别的属性和其他静态方法。它们可以与类的其他成员进行交互,但无法直接访问实例级别的成员。
-
基本用法
示例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。
- 基本用法
示例:使用抽象类进行多态
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()
,子类Cat
和Dog
分别实现了这个抽象方法。可以通过抽象类类型的变量来分别引用Cat
和Dog
类的对象,并调用它们的 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