装饰器用于修改或扩展函数或方法的行为。装饰器本质上是一个高阶函数,它可以接受一个或者多个函数作为参数,并返回一个新的函数。
装饰器通常定义一个外层函数,该外层函数返回一个内层函数。内层函数则负责执行实际的装饰逻辑。
def decorator(func):
def wrapper():
print('调用函数前加个打印功能1')
func()
print('调用函数后加个打印功能2')
return wrapper
@decorator
def say_hello():
print('say hello')
if __name__ == '__main__':
say_hello()
输出:
调用函数前加个打印功能1
say hello
调用函数后加个打印功能2
#装饰器的工作原理
(1)@decorator等价于 say_hello = my_decorator(say_hello)。
(2)调用过程:
say_hello 被传递给 my_decorator,my_decorator 返回 wrapper 函数。say_hello 现在指向 wrapper 函数。当调用 say_hello() 时,实际上是调用 wrapper()。
如果希望装饰器本身需要能接受参数,这可以通过再嵌套一层函数来实现。
# def decorator(func):
# def wrapper():
# print('调用函数前加个打印功能1')
# func()
# print('调用函数后加个打印功能2')
# return wrapper
#
#
# @decorator
# def say_hello():
# print('say hello')
def for_func(num):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@for_func(3)
def greet(name):
print(f"hello,{name}")
if __name__ == '__main__':
greet('wade')
输出:
hello,wade
hello,wade
hello,wade
使用装饰器后,原始函数的元数据(如名称、文档字符串等)会被覆盖。为了保留这些元数据,可以使用 functools.wraps。
def say_goodbye():
"""这是原函数"""
print("python is the best language in the world")
print(say_goodbye.__name__)
print(say_goodbye.__doc__)
输出:
say_goodbye
这是原函数
def decorator(func):
def wrapper(*args, **kwargs):
"""这是装饰器内层函数"""
print("调用原函数之前的操作")
result = func(*args, **kwargs)
print("调用原函数之后的操作")
return result
return wrapper
@decorator
def say_goodbye():
"""这是原函数"""
print("python is the best language in the world")
print(say_goodbye.__name__)
print(say_goodbye.__doc__)
输出:
wrapper
这是装饰器内层函数
import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""这是装饰器内层函数"""
print("调用原函数之前的操作")
result = func(*args, **kwargs)
print("调用原函数之后的操作")
return result
return wrapper
@decorator
def say_goodbye():
"""这是原函数"""
print("python is the best language in the world")
print(say_goodbye.__name__)
print(say_goodbye.__doc__)
输出:
say_goodbye
这是原函数
类装饰器就是使用类来实现的装饰器。它们通常通过在类中定义 __call__
方法来实现。当我们使用 @
语法应用装饰器时,Python 会调用装饰器类的 __init__
方法创建一个实例,然后将被装饰的函数或类作为参数传递给 __init__
方法。当被装饰的函数或方法被调用时,Python 会调用装饰器实例的 __call__
方法
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Before call")
result = self.func(*args, **kwargs)
print("After call")
return result
@MyDecorator
def hello():
print("Hello, world!")
输出:
Before call
Hello, world!
After call
在上述例子中,MyDecorator
类的实例被创建并传入hello
函数作为参数。当我们调用hello
时,实际上是在调用MyDecorator
实例的__call__
方法。
多个装饰器应用于同一个函数,装饰器的执行顺序是从上到下。
def decorator1(func):
def wrapper():
print("这是decorator 1装饰器")
func()
return wrapper
def decorator2(func):
def wrapper():
print("这是decorator 2装饰器")
func()
return wrapper
@decorator1
@decorator2
def say_goodbye():
"""这是原函数"""
print("python is the best language")
say_goodbye()
输出:
这是decorator 1装饰器
这是decorator 2装饰器
python is the best language
import logging
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_func_call(func):
def wrapper(*args, **kwargs):
logging.info(f"Calling {func.__name__} with args={args},kwargs={kwargs}")
result = func(*args, **kwargs)
logging.info(f"{func.__name__} returned {result}")
return result
return wrapper
@log_func_call
def add(a, b):
return a + b
# 调用函数
add(4, 5)
输出:
2024-11-03 23:10:18,295 - INFO - Calling add with args=(4, 5),kwargs={}
2024-11-03 23:10:18,295 - INFO - add returned 9
class Counter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数{self.func.__name__} 已经被调用{self.count}次")
return self.func(*args, **kwargs)
@Counter
def add(a, b):
return a + b
add(3, 5)
add(4, 9)
输出:
函数add 已经被调用1次
函数add 已经被调用2次
相比函数装饰器,类装饰器有几个主要优势。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。