Python中如何使用decorator函数
在Python中,decorator(装饰器)是一种Python函数,用于修改其他函数,通常称为目标函数。装饰器的主要作用是增强被修饰函数的功能,而不需要修改函数本身的源代码。这种思想是函数式编程的一种核心思想。
使用decorator的好处是,一方面可以让代码更加简洁、易于维护,另一方面可以实现代码的重用和模块化,避免代码中出现大量的重复代码。
如何定义一个decorator
在Python中,装饰器本身也是函数,其需要接收一个函数作为参数并返回一个新的函数,通常情况下,使用@符号来声明此函数是一个decorator。
下面是一个简单的装饰器范例:
def simple_decorator(func):
# 定义一个新的函数
def wrapper():
print('wrapper do some extra work')
# 执行函数
func()
return wrapper
# 被装饰的函数
def foo():
print('I am foo')
# 使用装饰器
foo = simple_decorator(foo)
# 调用被装饰函数
foo()
在上面的代码中,我们定义了一个简单的装饰器,其接收一个函数并返回一个新的函数。我们定义了一个被修饰的函数foo,接下来使用decorate来修饰这个函数。通过简单的调用foo()函数,实际上执行的是被装饰后的函数。
使用Python @语法糖
在Python中,我们可以使用语法糖@来更加便捷的使用装饰器。与上面的代码作比对,可以看出使用语法糖@可以让代码更加简洁。
def simple_decorator(func):
# 定义一个新的函数
def wrapper():
print('wrapper do some extra work')
# 执行函数
func()
return wrapper
@simple_decorator
def foo():
print('I am foo')
# 调用被装饰函数
foo()
使用注意事项
虽然使用decorator会让我们的代码更加简洁、易于维护,但我们也需要注意以下一些问题:
1. 保持被装饰函数的元数据。元数据指的是原函数的名称、文档和参数信息。在Python中,使用 @functools.wrapper 装饰器可以保证被装饰函数的元数据正确。
下面是一个元数据不保留的范例程序:
def memoize(func):
memo = {}
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = func(*args)
memo[args] = rv
return rv
return wrapper
@memoize
def fibonacci(n):
"""Return the nth fibonacci number."""
if n in (0, 1):
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci.__name__) # 输出wrapper
上面的示例展示了如何使用缓存装饰器来计算斐波那契数列。而这个示例程序有一个问题,原始的fibonacci函数文档和名称被装饰函数覆盖了。修改如下:
import functools
def memoize(func):
memo = {}
@functools.wraps(func) # 加上这行保持元数据
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = func(*args)
memo[args] = rv
return rv
return wrapper
@memoize
def fibonacci(n):
"""Return the nth fibonacci number."""
if n in (0, 1):
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci.__name__)
在这段修改后的代码中,我们使用了@functools.wraps装饰器,用于保留原始函数的元数据。现在输出的就是"fibonacci"了。
2. 装饰器作用域问题。当我们使用装饰器修饰函数时,装饰器会在全局作用域中创建一个新的函数定义,从而影响被修饰函数的作用域。
# 不带参数的装饰器
def simple_decorator(func):
print("官网:https://python.org")
# 定义一个新函数
def wrapper(*args, **kwargs):
print('wrapper()中执行语句')
return func(*args, **kwargs)
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
# 调用函数
say_hello()
output:
官网:https://python.org
wrapper()中执行语句
Hello!
如果您装饰带有参数的函数,则需在wrapper函数中添加必要的参数。
# 带参数的装饰器
def simple_decorator(func):
# 定义一个新函数
def wrapper(*args, **kwargs):
print('wrapper()中执行语句')
return func(*args, **kwargs)
return wrapper
@simple_decorator
def say_hello(name):
print(f"Hello, {name}!")
say_hello("world")
output:
wrapper()中执行语句
Hello, world!
在上述代码中,我们定义了一个带有参数的被装饰函数,并通过装饰器来修改这个函数。在这个代码中,装饰器中定义的wrapper函数需要接收和被装饰函数一样的参数,并且返回值也是一样的。
3. 保留装饰器状态。有时我们需要通过装饰器来记录状态,比如计数器。在这种情况下,我们需要将装饰器的状态记录下来,从而保证在下一次使用装饰器时不会重置状态。下面是一个简单的装饰器,用于记录函数运行次数。
def count_calls(func):
def wrapper(*args, **kwargs):
wrapper.num_calls += 1
return func(*args, **kwargs)
wrapper.num_calls = 0
return wrapper
@count_calls
def foo():
print("Hello, World!")
foo()
foo()
print(f"函数执行了{foo.num_calls}次.")
Hello, World!
Hello, World!
函数执行了2次.
最后总结
Python中的Decorator是一个非常有用的编程思想,它可以在不修改原始代码的情况下增强函数和类的功能。Decorators 支持代码的重用和模块化,提高了代码的可读性、可维护性和易用性。当您学会了如何正确地使用Decorators 之后,它的功能可以帮助您写出高质量的Python代码,让你的值更加清晰明了,同时也可以让你的程序更加优雅和好维护。
