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)
函数 sumab 被 pretty_sumab 包装了,这一点通过其上方的 @ 符号表明。
当你调用 sumab 函数时,会同时执行 sumab 和 pretty_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"
这里就使用了 @ 符号进行装饰。