Python 装饰器入门

更新于 2026-01-12

Python 装饰器入门

使用装饰器(Decorators)可以为已有函数添加新功能,这被称为元编程(metaprogramming)。

装饰器本质上是一个函数:它接收一个函数作为参数(即被装饰的函数),并返回该函数(可能带有扩展功能,也可能没有)。

在某些时候,这种扩展功能的方式非常有用。我们将在本文后面展示一些真实世界的例子。

函数也是对象

在 Python 中,一切皆为对象,包括函数。这意味着函数可以像其他对象一样被传递和返回。初次看到下面的代码时,你可能会觉得有些奇怪:

def hello():
    print("Hello")

# 即使是函数也是对象
message = hello

# 调用新函数
message()

无论是调用 message() 还是 hello(),输出结果都是一样的。因为它们指向的是同一个函数对象。

现在,让我们继续学习装饰器。

装饰器(Decorators)

示例 1

装饰器接收一个函数,对其进行扩展,并返回它。没错,函数可以返回另一个函数

def hello(func):
    def inner():
        print("Hello ")
        func()
    return inner

def name():
    print("Alice")

obj = hello(name)
obj()

在上面的例子中,hello() 就是一个装饰器。

语句:

obj = hello(name)

表示函数 name() 被函数 hello() 装饰了。
也就是说,name 被包装进了另一个函数中。

示例 2

函数可以通过包装的方式进行扩展。

def who():
    print("Alice")

def display(func):
    def inner():
        print("The current user is : ", end="")
        func()
    return inner

if __name__ == "__main__":
    myobj = display(who)
    myobj()

在这个例子中,函数 who()display() 装饰了。

语法糖(Syntactic Sugar)

装饰器很常用,Python 提供了一种更简洁的写法。虽然效果完全相同,但代码更清晰。

你可以使用 @ 符号来简化装饰器的使用:

@hello
def name():
    print("Alice")

if __name__ == "__main__":
    name()

这段代码的输出与前面完全一致,但写法更干净。

请注意:
调用

@hello
def name():

实际上等价于:

obj = hello(name)

两种写法都是将装饰器应用到函数上。

带参数的装饰器

装饰器也可以处理带参数的函数。例如,你有一个函数用于打印 a + b 的和:

def sumab(a,b):
    summed = a + b
    print(summed)

你可以用装饰器将其包装起来。下面展示了如何实现:

def pretty_sumab(func):
    def inner(a,b):
        print(str(a) + " + " + str(b) + " is ", end="")
        return func(a,b)

    return inner

@pretty_sumab
def sumab(a,b):
    summed = a + b
    print(summed)

if __name__ == "__main__":
    sumab(5,3)

函数 sumabpretty_sumab 包装了,这一点通过其上方的 @ 符号表明。

当你调用 sumab 函数时,会同时执行 sumabpretty_sumab 中的逻辑,并正确传递参数。

真实世界中的应用示例

使用场景:执行时间测量

装饰器可用于测量函数的执行时间。

假设你定义了一个简单的休眠函数:

def myFunction(n):
    time.sleep(n)

只需在函数上方添加一行 @measure_time,就可以测量其运行时间。

完整示例如下:

import time

def measure_time(func):
    def wrapper(*arg):
        t = time.time()
        res = func(*arg)
        print("Function took " + str(time.time()-t) + " seconds to run")
        return res

    return wrapper

@measure_time
def myFunction(n):
    time.sleep(n)

if __name__ == "__main__":
    myFunction(2)

这段代码会输出函数 myFunction() 的执行时间。
最酷的是,仅需添加一行代码 @measure_time,我们就能轻松测量程序的执行耗时。

使用场景:Web 应用

在 Web 应用开发中,装饰器也非常常见。以 Flask 框架为例,你经常需要定义 URL 路由。

每个路由对应 Web 应用中的一个页面。
例如,访问 /about 页面时,会调用 about_page() 函数:

@app.route("/about")
def about_page():
    return "Website about nachos"

这里就使用了 @ 符号进行装饰。