装饰器(Decorators)是Python的⼀个重要部分。简单地说:他们是修改其他函数的功能的函 数。他们有助于让我们的代码更简短,也更Pythonic。
在上一个例子中,其实我们已经创建了一个装饰器。有一个函数 ,他接受的参数是一个函数。
But!:上述代码中,当使用doSomethingBeforeHi(hi)的时候,没有定义一个新的函数,而是直接执行了!因此我们需要新的代码组织方式,来返回一个新的函数,方法就是在函数内定义一个函数,最后返回。
这样就可以返回一个用装饰器装饰后的函数,要变成常见的装饰器形式@,只需要:
装饰器装饰的函数应该是任意的,因此装饰器内a_func的参数是无法预料的,这时候我们就需要使用*args
和**kwargs
。
output:
before a_func
I'm a_func 1
after a_func
我们可以发现,在装饰前后,a_func接受的参数是不变的,a_func_dec在被装饰前和装饰后都只接收一个int,装饰器应该是无感的。(当然也可以写成不一样的,但是不太好)
@wraps(func)
是一个用于装饰器的装饰器,通常用于保留原始函数的元数据(如函数名、文档字符串等)。这是一种良好的习惯,可以确保装饰后的函数在行为上与原始函数一致,同时保留原始函数的信息。通常应该在定义自己的装饰器时使用 @wraps(func)
。
示例:
from functools import wraps
def decorator(a_func):
@wraps(a_func)
def wrapTheFunction(*args, **kwargs):
print("before a_func")
a_func(*args, **kwargs)
print("after a_func")
return wrapTheFunction
@decorator
def a_func_dec(num:int):
print("I'm a_func", num)
a_func_dec(1)
print(a_func_dec.__name__)
output:
before a_func
I'm a_func 1
after a_func
a_func_dec
在上面的示例中,@wraps(func)
装饰器确保装饰后的函数 wrapper
保留了原始函数 a_func_dec
的函数名和文档字符串。这使得装饰后的函数在行为上更像原始函数,同时不会丢失有用的元数据信息。
在编写自定义装饰器时,使用 @wraps
是一种良好的实践,以确保你的装饰器不会破坏原始函数的元数据。
from functools import wraps
def decorator_name(f):
@wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
@decorator_name
def func():
return("Function is running")
print(func())
当我们使用装饰器放于一个函数前时,默认第一个参数就是这个函数,因此我们不需要再输入新的参数,但是如果有其他的参数,我们就需要像函数调用的方式写在装饰器后。
可能显而易见的会想到,能不能直接接在ecorator_name(f)的f后面,这样是可以的,但是之后就必须得使用显示声明参数,
def repeat_decorator(func, num_repeats):
def wrapper(*args, **kwargs):
results = []
for _ in range(num_repeats):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
# 使用装饰器并传递参数
@repeat_decorator(num_repeats=3)
def greet(name):
return f"Hello, {name}!"
results = greet("Alice")
print(results)
因此我们需要别的方法来实现带参数的装饰器,一般我们使用如下方法,三层函数嵌套,最外层是装饰器的参数接收,第二层是装饰器,接收被装饰的函数,最内层是装饰完的函数,并最终被返回。
例一:
def repeat_decorator(num_repeats):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(num_repeats):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat_decorator(3) # 使用装饰器并传递参数
def greet(name):
return f"Hello, {name}!"
results = greet("Alice")
print(results)
例二:
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写⼊内容
with open(logfile, 'a') as opened_file:
# 现在将⽇志打到指定的logfile
opened_file.write(log_string + '\n')
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在⼀个叫做 out.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
是不是感觉这样就有点复杂了?三层函数嵌套,这时候我们就可以使用类来构建装饰器。
Python中,当调用对象名的时候,会调用__call__方法,因此我们联想到使用类来充当装饰器的效果。
class MyCallableClass:
def __init__(self, value):
self.value = value
def __call__(self, x):
return self.value * x
# 创建一个类的实例
callable_instance = MyCallableClass(5)
# 使用实例像调用函数一样
result = callable_instance(10)
print(result) # 输出 50
类装饰器例:
class MyDecorator:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self, func):
def wrapped_function(*args, **kwargs):
result = func(*args, **kwargs)
return f"{self.prefix}: {result}"
return wrapped_function
# 使用类装饰器
@MyDecorator("Result")
def greet(name):
return f"Hello, {name}!"
result = greet("Alice")
print(result)
@MyDecorator("Result")
在实例化一个对象之后,对象调用__call__
方法,__call__
方法是一个装饰器,返回装饰后的函数。
装饰器的参数写在__init__
里,装饰器的内容写在__call__
内,当装饰器比较复杂的时候,使用装饰器类更加简洁明了。