欢迎访问宙启技术站
智能推送

装饰器:Python中高级函数的应用

发布时间:2023-06-24 16:40:15

在Python中,装饰器是一种高级的编程技术,它可以用来修改或扩展函数或类的功能,而不需要修改原有的代码。它可以使代码更优雅和简洁,也方便了我们代码的管理和维护。本文将详细介绍装饰器的定义、创建和使用方法,并给出一些具体的实例。

1. 装饰器的定义

装饰器是一个可调用的对象(函数、类、方法或类方法),它可以将一个函数或类添加到另一个函数、类或对象中,也可以修改、扩展或删除已有的函数或类的功能。这样,我们就可以在不改变原有的代码的情况下,灵活地改变它的行为。

2. 装饰器的创建方法

装饰器的创建方法是使用 @ 符号和装饰器函数来装饰一个函数或类。这里是一个简单的例子:

示例1:

def my_decorator(func):
    def wrapper():
        print('Before the function is called.')
        func()
        print('After the function is called.')
    return wrapper

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

# 调用装饰后的被装饰函数
say_hello()

在这个例子中,我们定义了一个函数 my_decorator ,它的参数是一个函数。函数 my_decorator 返回了一个内部定义的函数 wrapper ,该函数包装了传入的函数。我们使用 @ 符号和 my_decorator 装饰函数 say_hello。这意味着我们已经将 say_hello 传递给 my_decorator 函数,并使用返回的 wrapper 函数替换了原来的 say_hello 函数。现在每当我们调用装饰过的 say_hello 函数时,它会自动调用 my_decorator 函数,并在其前后打印一条消息。

以下是输出结果:

Before the function is called.
Hello, world!
After the function is called.

这正是我们预期的输出结果,这个装饰器成功地将被装饰函数 say_hello 的功能加强了。

我们也可以把装饰器加到类上,例如:

示例2:

def my_decorator(cls):
    class Wrapper:
        def __init__(self, *args):
            self.wrapped = cls(*args)
        def display(self):
            print("Wrapped class instance: ", self.wrapped)
    return Wrapper

@my_decorator
class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def display(self):
        print("MyClass instance x={}, y={}".format(self.x, self.y))

# 创建装饰后的对象,并调用 display 函数
obj = MyClass(10, 20)
obj.display()

在这个例子中,我们定义了一个类装饰器 my_decorator ,它的参数是一个已有的类。该装饰器定义了一个内部类Wrapper,创建了一个实例变量 wrapped,它包含了传入的 类的实例。 我们使用 @ 符号和 my_decorator 装饰类 MyClass。这意味着我们已经将 MyClass 传递给 my_decorator 函数,并使用返回的 Wrapper 类替换了原来的 MyClass 类。现在每当我们创建 MyClass 的实例 obj,并调用它的 display 函数时,它会自动调用 my_decorator 函数,并使用 Wrapper 类来包装 MyClass 的实例。

以下是输出结果:

Wrapped class instance:  <__main__.MyClass object at 0x108d3f950>

这是我们预期的输出结果,这个装饰器成功地将类 MyClass 的功能扩展了。

3. 装饰器的应用场景

装饰器可以应用于各种场景。以下是一些示例:

3.1 应用场景一:性能测试

我们可以使用装饰器来测量代码块的运行时间。下面是一个简单的示例:

import time

def measure_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("Execution time: ", end_time - start_time)
        return result
    return wrapper

@measure_time
def test1(n):
    for i in range(n):
        pass

test1(1000000)

在这个例子中,我们定义了一个函数装饰器 measure_time ,它可以测量函数的执行时间。我们使用 @ 符号和 measure_time 装饰函数 test1。现在每当我们调用 test1 函数时,它会自动调用 measure_time 函数,并测量 test1 函数的执行时间。

以下是输出结果:

Execution time:  0.027861833572387695

这是我们预期的输出结果,它显示 test1 函数的执行时间为0.02786秒。

3.2 应用场景二:输入检查

我们可以使用装饰器来检查函数参数的类型和值。下面是一个简单的示例:

def check_input(func):
    def wrapper(*args, **kwargs):
        for i in args:
            if not isinstance(i, int):
                raise TypeError("All arguments must be int")
        if kwargs.get('x') is not None and kwargs['x'] < 0:
            raise ValueError("x must be positive")
        if kwargs.get('y') is not None and kwargs['y'] < 0:
            raise ValueError("y must be positive")
        return func(*args, **kwargs)
    return wrapper

@check_input
def add(x, y=0):
    return x + y

print(add(1, 2))
print(add('1', 2))
print(add(1, y=-2))

在这个例子中,我们定义了一个函数装饰器 check_input ,它进行了输入检查。如果参数不符合要求,它会引发 TypeError 或 ValueError 异常。我们使用 @ 符号和 check_input 装饰函数 add。现在每当我们调用 add 函数时,它会自动调用 check_input 函数,并检查输入参数的合法性。

以下是输出结果:

3
Traceback (most recent call last):
  File "example.py", line 17, in <module>
    print(add('1', 2))
  File "example.py", line 6, in wrapper
    raise TypeError("All arguments must be int")
TypeError: All arguments must be int

Traceback (most recent call last):
  File "example.py", line 18, in <module>
    print(add(1, y=-2))
  File "example.py", line 10, in wrapper
    raise ValueError("y must be positive")
ValueError: y must be positive

这是我们预期的输出结果,加法操作成功地返回x,y的和,并检测到非法输入类型('1')和非法的输入值(y=-2)。

3.3 应用场景三: 数据缓存

我们可以使用装饰器来缓存计算结果,这样可以加速重复计算的过程。下面是一个简单的示例:

def cache_data(func):
    data = {}
    def wrapper(*args):
        if args in data:
            print("Retrieve data from cache: ", args)
            return data[args]
        result = func(*args)
        print("Save data to cache: ", args)
        data[args] = result
        return result
    return wrapper

@cache_data
def calculate(x, y):
    return x + y

print(calculate(1, 2))
print(calculate(1, 2))
print(calculate(3, 4))
print(calculate(1, 2))

在这个例子中,我们定义了一个函数装饰器 cache_data ,它可以缓存函数的计算结果。变量 data 用于存储事先计算好的结果。当一个参数组(例如 (1,2))被传递给函数时,在缓存检查中查找该组是否已计算。如果否,计算新结果并将新结果保存到缓存中。如果