装饰器是一种在不修改函数的前提下为函数增加额外功能的机制。它是通过在函数前面加上一个装饰器函数来实现的。
举个例子,假设我们有一个函数 say_hello
,它打印出 “Hello, world!”。我们希望在这个函数执行之前和之后都能打印一条消息。
我们可以使用装饰器来实现这个功能:
def logger(func):
def wrapper(*args, **kwargs):
print("Before calling function")
result = func(*args, **kwargs)
print("After calling function")
return result
return wrapper
@logger
def say_hello():
print("Hello, world!")
say_hello()
输出结果如下:
Before calling function
Hello, world!
After calling function
我们可以看到,在调用 say_hello
函数之前和之后都有一条消息被打印出来。这就是装饰器的作用。
装饰器的语法是在函数定义之前加上一个 @decorator
语句,其中 decorator
是装饰器函数的名称。在这个例子中,我们使用了 @logger
装饰器,它会在函数被调用之前和之后打印消息。
装饰器是一种非常有用的工具,它可以让我们在不修改函数的情况下为函数增加额外的功能。它们常用于日志记录、权限检查和其他类似的场景。
装饰器可以接受参数,例如:
def logger(prefix):
def decorator(func):
def wrapper(*args, **kwargs):
print(prefix, "Before calling function")
result = func(*args, **kwargs)
print(prefix, "After calling function")
return result
return wrapper
return decorator
@logger("DEBUG")
def say_hello():
print("Hello, world!")
say_hello()
输出结果如下:
DEBUG Before calling function
Hello, world!
DEBUG After calling function
可以看到,我们在装饰器函数 logger
中传入了一个参数 prefix
,然后在装饰器内部的包装函数 wrapper
中使用了这个参数。这样,我们就可以根据需要在装饰器中使用参数了。
装饰器还有一个常见的用法是将它们嵌套使用。例如,我们可以定义一个装饰器 decorator_a
,然后在这个装饰器的内部使用另一个装饰器 decorator_b
。
例如:
def decorator_a(func):
def wrapper(*args, **kwargs):
print("Decorator A")
return func(*args, **kwargs)
return wrapper
def decorator_b(func):
def wrapper(*args, **kwargs):
print("Decorator B")
return func(*args, **kwargs)
return wrapper
@decorator_a
@decorator_b
def say_hello():
print("Hello, world!")
say_hello()
输出结果如下:
Decorator B
Decorator A
Hello, world!
可以看到,当我们调用 say_hello
函数时,两个装饰器都会执行。这样,我们就可以使用嵌套的装饰器来扩展函数的功能。
装饰器还可以通过使用 functools.wraps
函数来保留原函数的元信息,例如函数名称、文档字符串和参数列表等。这样,在使用装饰器修饰函数之后,函数的元信息仍然是正确的。
例如:
import functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Before calling function")
result = func(*args, **kwargs)
print("After calling function")
return result
return wrapper
@logger
def say_hello():
"""Say hello to the world"""
print("Hello, world!")
print(say_hello.__name__) # Outputs: "say_hello"
print(say_hello.__doc__) # Outputs: "Say hello to the world"
可以看到,使用了 functools.wraps
函数之后,函数的名称和文档字符串仍然是正确的。这样,我们就可以在使用装饰器的同时保留原函数的元信息了。
在 Python 中,装饰器也可以用于类。例如,我们可以定义一个装饰器,在类定义之前使用它来修改类的属性或方法。
例如:
def logger(cls):
cls.logged = True
return cls
@logger
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
print(obj.logged) # Outputs: True
在这个例子中,我们定义了一个装饰器 logger
,它会在类定义之前修改类的 logged
属性。然后,我们使用了 @logger
装饰器来修饰类 MyClass
。这样,在创建 MyClass
的实例时,这个实例就会具有 logged
属性。
同样的,我们也可以在装饰器中使用参数,例如:
def logger(prefix):
def decorator(cls):
cls.logged = True
cls.prefix = prefix
return cls
return decorator
@logger("DEBUG")
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
print(obj.logged) # Outputs: True
print(obj.prefix) # Outputs: "DEBUG"
在这个例子中,我们的装饰器可以接受一个参数 prefix
,然后在装饰器内部使用这个参数来修改类的属性。这样,我们就可以在使用装饰器的同时传入参数了。
总的来说,装饰器是一种非常有用的工具,它可以帮助我们为函数增加额外的功能,同时还可以保留原函数的元信息。它们广泛应用于 Python 编程中,常用于日志记录、权限检查和其他类似的场景。