1. 什么是装饰器
在介绍 python 装饰器的功能与用法案例详解前,我们首先需要了解什么是装饰器。在编程中,装饰器是一种函数或类,它允许我们在代码运行时动态地修改、扩展或包装其他函数、类或方法的行为。相当于在原有的代码结构上进行了装饰,为函数或类添加了额外的功能。
如下是一个简单的例子,定义了一个被装饰的函数,对其进行了装饰器的操作:
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
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)
say_hello()
在这个例子中,我们先定义了一个装饰器函数 my_decorator
。这个函数接受一个函数作为参数,然后返回另一个函数 wrapper
,并在 wrapper
中添加了一些额外的代码,最后返回 wrapper
。
我们再定义了一个函数 say_hello
,并把它传递给 my_decorator
函数,然后把返回的函数重新赋值给 say_hello
。最后调用 say_hello
函数,此时实际上调用的是 wrapper
函数。
由此可见,装饰器实质上就是一个对原有函数或类的包装,它允许我们在不改变原有代码结构的情况下修改其行为。
2. 装饰器的语法
2.1 函数装饰器
下面我们来看看装饰器的语法。
函数装饰器的语法如下:
@decorator
def func():
pass
其中,decorator
是装饰器函数,它接受一个函数作为参数,然后返回另一个函数。
例如,我们可以使用下面的装饰器函数来对函数的执行时间进行计时:
import time
def my_timer(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("Time used:", end_time - start_time)
return wrapper
@my_timer
def say_hello():
time.sleep(1)
print("Hello!")
say_hello()
在这个例子中,我们定义了一个装饰器函数 my_timer
,它接受一个函数作为参数,然后返回另一个函数 wrapper
。在 wrapper
中记录了函数执行前后的时间,并打印出执行时间。
然后我们使用 @my_timer
来装饰函数 say_hello
,相当于执行了下面的语句:say_hello = my_timer(say_hello)
。
当我们调用 say_hello
函数时,会先执行 my_timer
函数,并返回 wrapper
,然后再执行 wrapper
函数,最终打印出执行时间。
2.2 类装饰器
除了函数装饰器外,还可以使用类装饰器,它是一个类,接受一个函数作为参数,然后提供了一些额外的功能。
例如,我们可以使用下面的类装饰器来对函数进行缓存:
class Cache:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if args in self.cache:
return self.cache[args]
else:
result = self.func(*args)
self.cache[args] = result
return result
@Cache
def fibonacci(n):
if n in (0, 1):
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
在这个例子中,我们定义了一个类装饰器 Cache
,它接受一个函数作为参数,并提供了一个 __call__
方法来代替原来的函数。在 __call__
中,它先检查缓存中是否已经存在相同参数的计算结果,如果存在,则直接返回缓存中的结果,否则计算结果,并将结果存入缓存中。
我们使用 @Cache
来装饰函数 fibonacci
,相当于执行了下面的语句:fibonacci = Cache(fibonacci)
。
当我们调用 fibonacci
函数时,会先执行 Cache
类的 __init__
方法,并保存被装饰的函数和一个空缓存。然后再执行 fibonacci
函数,实际上是执行了 Cache
类的 __call__
方法,并返回计算结果。
3. 装饰器的应用
3.1 函数调试
函数调试是编写代码时必不可少的一部分,当我们运行一个程序时,总会遇到一些错误,此时就需要进行调试。
下面是一个使用装饰器来进行函数调试的例子:
def debug(func):
def wrapper(*args, **kwargs):
print("Calling function:", func.__name__)
print("Args:", args)
print("Kwargs:", kwargs)
result = func(*args, **kwargs)
print("Result:", result)
return result
return wrapper
@debug
def add(x, y):
return x + y
add(2, 3)
在这个例子中,我们定义了一个装饰器函数 debug
,它接受一个函数作为参数,并返回另一个函数 wrapper
。在 wrapper
中,它先打印出函数名、参数以及关键字参数,然后执行函数,并打印出结果。
我们使用 @debug
来装饰函数 add
,相当于执行了下面的语句:add = debug(add)
。
当我们调用 add
函数时,会先执行 wrapper
函数,并打印出函数名、参数以及关键字参数,然后再执行原有的 add
函数,最后打印出结果。
3.2 缓存函数计算结果
有些函数的计算结果是比较耗时的,例如递归计算斐波那契数列。如果经常调用这个函数,并且参数较大,那么计算时间将会非常长。
我们可以使用装饰器来对函数进行缓存,如果传入相同的参数,则直接返回缓存中的结果,这样可以显著提高函数的执行效率。
下面是一个使用装饰器来缓存函数计算结果的例子:
class Memoize:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if args in self.cache:
return self.cache[args]
else:
result = self.func(*args)
self.cache[args] = result
return result
@Memoize
def fibonacci(n):
if n in (0, 1):
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
在这个例子中,我们定义了一个类装饰器 Memoize
,它接受一个函数作为参数,并提供了一个 __call__
方法来代替原来的函数。在 __call__
中,它先检查缓存中是否已经存在相同参数的计算结果,如果存在,则直接返回缓存中的结果,否则计算结果,并将结果存入缓存中。
我们使用 @Memoize
来装饰函数 fibonacci
,相当于执行了下面的语句:fibonacci = Memoize(fibonacci)
。
当我们调用 fibonacci
函数时,会先执行 Memoize
类的 __init__
方法,并保存被装饰的函数和一个空缓存。然后再执行 fibonacci
函数,实际上是执行了 Memoize
类的 __call__
方法,并返回计算结果。
3.3 函数授权
在一些情况下,有些人可能并不具备调用某个函数的权限,此时我们可以使用装饰器来授权。
下面是一个使用装饰器来授权的例子:
def check_permission(permission):
def decorator(func):
def wrapper(*args, **kwargs):
if permission == "admin":
return func(*args, **kwargs)
else:
raise ValueError("Permission denied")
return wrapper
return decorator
@check_permission("admin")
def add_user(username):
print("Adding user:", username)
add_user("Alice")
在这个例子中,我们定义了一个装饰器函数 check_permission
,它接受一个参数 permission
,返回一个装饰器函数,这个装饰器函数接受一个函数作为参数,并返回另一个函数 wrapper
。在 wrapper
中,它首先检查传入的 permission
是否为 "admin",如果是,则执行原有的函数 func
,否则抛出异常。
我们使用 @check_permission("admin")
来装饰函数 add_user
,相当于执行了下面的语句:add_user = check_permission("admin")(add_user)
。
当我们调用 add_user
函数时,会先执行 check_permission("admin")
函数,返回一个装饰器函数,并将其应用于 add_user
函数上。
最终,当我们调用 add_user
函数时,会先执行 wrapper
函数,检查权限,然后再执行原有的 add_user
函数。
4. 总结
装饰器是 Python 中非常强大的语法,它允许我们在不改变原有代码结构的情况下,为函数或类添加额外的功能。本文介绍了装饰器的语法和几种常见的应用,例如函数调试、缓存函数计算结果、函数授权等。通过学习本文,相信读者已经对装饰器有了更加深入的了解,并可以利用装饰器来更好地编写 Python 代码。