详解Python 装饰器

  • Post category:Python

Python 装饰器是 Python 革命性的面向对象语言特性之一。它是一种特殊类型的函数,主要用于修改其他函数的行为。在正式讲解装饰器使用方法之前,我们需要了解一些定义:

  • 函数:在 Python 中函数相当于一组可执行的语句,可以在代码中多处调用,同时可以传入参数并返回值。
  • 装饰器:在 Python 中,装饰器的作用是在定义函数时修改函数的行为。装饰器本质上是一个 Python 函数,可以接收其他函数作为参数,并返回一个新的函数,同时还有一个常见的 @语法糖 用于简化装饰器的使用。

下面,我将从定义、分类、应用等方面,详细讲解 Python 装饰器的使用方法。

1. 装饰器基础

Python 装饰器本质上就是一个函数,它可以接收一个函数并返回一个函数。使用装饰器的语法很简单,只需要使用 Python 的 @语法糖 将装饰器与函数进行关联即可。

例如下面是一个简单的例子:

def my_decorator(func):
    def wrapper():
        print("Executing the decorated function...")
        return func()
    return wrapper

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

greet()

输出结果为:

Executing the decorated function...
Hello!

在这个例子中,函数 my_decorator 是一个装饰器。它接收一个参数 func,其中 func 是一个函数。并返回一个新的函数 wrapper

wrapper 中,首先输出一条语句,然后返回真正想执行的函数 func()。装饰器会将这个新的函数 wrapper 返回给原始的函数 greet

最后,在函数 greet 上使用装饰器 @my_decorator 声明,相当于做了以下事情:

greet = my_decorator(greet)

这段代码相当于将原始的函数 greet 赋值为一个名为 wrapper 的新函数。最终,执行函数 greet() 时返回的是 wrapper() 函数。

2. 装饰器分类

在 Python 中装饰器可以分为两种类型:函数装饰器和类装饰器。

函数装饰器

函数装饰器是指可以直接装饰普通函数的装饰器,其基本格式如下:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # 新增的功能
        return func(*args, **kwargs)
    return wrapper

其中 func 表示被装饰的函数,wrapper 是装饰器添加的功能,*args**kwargs 则表示被装饰函数的参数和关键字参数。

下面是一个函数装饰器的示例,可以用于计算程序执行时间:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("函数执行时间:{} 秒".format(end_time-start_time))
        return result
    return wrapper

@timer
def my_function():
    time.sleep(2)
    print("Function executed.")

my_function()

输出结果为:

Function executed.
函数执行时间:2.004966974258423 秒

在这个示例中,装饰器 @timer 可以用来跟踪被装饰函数 my_function() 执行需要的时间。

类装饰器

类装饰器是指以类的形式装饰函数,其基本格式如下:

class MyDecorator:
    def __init__(self, f):
        self.f = f

    def __call__(self):
        # 执行被修饰函数的代码
        self.f()

在上面的示例中,__call__() 方法是一个特殊方法。当类实例化后且被使用时,就会自动调用该方法。

下面是一个使用类装饰器的示例:

class DecoratorClass:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        print("Before the function is called.")
        self.func(*args)
        print("After the function is called.")

@DecoratorClass
def my_function(name):
    print("Function executed with name:", name)

my_function("John")

输出结果为:

Before the function is called.
Function executed with name: John
After the function is called.

在这个示例中,使用了一个名为 DecoratorClass 的类装饰器。它在函数调用前和调用后分别输出两条语句。并且将名字作为参数传入被装饰函数 my_function(),完成函数的执行。

3. 高级用法

使用 Python 装饰器,可以实现很多有趣有用的功能。包括缓存数据、检查函数参数类型、验证用户登录等等。下面介绍两个比较有用的高级用法。

链式装饰器

Python 装饰器还支持链式装饰器,即在同一个函数上使用多个装饰器。下面是一个链式装饰器的示例:

def make_bold(func):
    def wrapper():
        return "<b>" + func() + "</b>"
    return wrapper

def make_italic(func):
    def wrapper():
        return "<i>" + func() + "</i>"
    return wrapper

@make_bold
@make_italic
def hello():
    return "Hello, World!"

print(hello())

输出结果为:

<b><i>Hello, World!</i></b>

在这个示例中,定义了两个装饰器 make_boldmake_italic。其中 make_bold 装饰器返回将被装饰函数的返回值加粗标签,make_italic 装饰器返回被装饰函数的返回值加上斜体标签。

在函数 hello() 上使用了两个装饰器作为函数的修饰,其中先用 @make_italic 装饰器修饰 hello() 函数,然后再用 @make_bold 装饰器为修饰过的函数再次加上一个加粗函数修饰。

带参数的装饰器

装饰器还可以带参数,这样可以在装饰器中实现更加灵活的功能。例如下面是一个带有参数的装饰器的示例:

def prefix_decorator(prefix):
    def decorator_function(func):
        def wrapper_function(*args, **kwargs):
            print(prefix, "started.")
            func(*args, **kwargs)
            print(prefix, "finished.")
        return wrapper_function
    return decorator_function

@prefix_decorator("LOG")
def print_hello():
    print("Hello, World!")

print_hello()

输出结果为:

LOG started.
Hello, World!
LOG finished.

在这个示例中,定义了一个带有参数的装饰器 prefix_decorator。它接收一个字符串 prefix 作为参数,并返回一个装饰器 decorator_function。其中的 decorator_function 则返回一个新的函数 wrapper_function,该函数用于在被装饰函数执行前与执行后打印 prefix 的输出。

最后,在函数调用时使用 @prefix_decorator("LOG") 语法来指定 prefix 的值。这样,每次调用 print_hello() 函数时都会打印 LOG started.LOG finished. 的输出。

4. 总结

以上就是 Python 装饰器的完整攻略了。本文围绕装饰器的基础、分类和高级用法展开,前半部分主要是介绍基本语法和使用方法,后半部分则是几个比较有用的高级应用。

当然,想深入学习 Python 装饰器知识的读者可以进一步探索 Python 官方文档中有关基于函数实现的装饰器和基于类实现的装饰器的详细内容。