在 Python 中,装饰器(decorator)是一种特殊的函数或类,用于在不修改原始代码的情况下,动态地修改或增强函数、方法或类的行为。

函数:独立的代码块,可以在任何地方定义和调用。

方法:类中的函数,属于类的实例或类本身。

:用于定义对象的蓝图,包含属性和方法。

下面将记录学习过程中,遇到的常见装饰器用法。

1. 函数装饰器

在定义普通函数装饰器的过程中,一个装饰器通常是一个函数,接收另一个函数作为参数,并返回一个新的函数。这个新函数包含了对原始函数的调用以及新增的功能。

1.1 示例用法

  • 无参数示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.
  • 有参数示例 (记录日志)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} is called with {args} and {kwargs}")
        return func(*args, **kwargs)
    return wrapper

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

add(3, 4)
# Function add is called with (3, 4) and {}
# 7

*args**kwargs 用于处理不定数量的参数。

  • 前者将可变数量的非关键字参数打包成一个元组;
  • 后者将可变数量的关键字参数打包成一个字典。

2. 方法装饰器

方法装饰器是用于装饰类方法的函数,它们可以在不修改方法代码的情况下扩展或改变方法的行为。

2.1 @staticmethod与@classmethod

  • @staticmethod:定义静态方法,不需要传递实例 (self) 或类 (cls) 作为参数。
1
2
3
4
5
6
7
8
class Calculator:
    @staticmethod
    def add(x, y):
        return x + y
# 直接通过类名 Calculator 调用 add 方法
Calculator.add(5, 3)
# 先创建 Calculator 类的一个实例,再通过实例调用 add 方法。
Calculator().add(5, 3)
  • @classmethod:用于定义类方法,类方法的第一个参数是类对象 (cls),而不是实例 (self)。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def from_tuple(cls, coords):
        return cls(coords[0], coords[1])

    @classmethod
    def from_string(cls, coord_string):
        x, y = map(int, coord_string.split(','))
        return cls(x, y)

# 使用不同的类方法实例化对象
point1 = Point.from_tuple((3, 4))
print(point1.x, point1.y)  # 输出:3 4

point2 = Point.from_string("5,6")
print(point2.x, point2.y)  # 输出:5 6

2.2 @property与@xxx.setter

  • @property 将一个方法转换为属性调用,不需要加括号。
  • @xxx.setter 可以限定修改范围
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyClass:
    def __init__(self, value):
        self._value = value

    @property
    def value(self):
        print("Hello")
        return self._value

    @value.setter 
    def value(self, new_value):
        if new_value < 0:  #限定条件
            raise ValueError("Value cannot be negative")
        self._value = new_value

test = MyClass(5)
test.value
# Hello
# 5
test.value = 100
test.value
# Hello
# 100

3. 类装饰器

通过类装饰器,可以在实例化类对象时,自动地修改类的行为或属性。

3.1 示例用法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def add_method(cls):
    cls.new_method = lambda self: print("New method added!")
    return cls

@add_method
class MyClass:
    def original_method(self):
        print("Original method")

obj = MyClass()
obj.original_method()
# Original method

obj.new_method()
# New method added!