1. 装饰器概念解析
装饰器是 Python 中高级语言特性之一,指的是将一个函数作为参数,并返回另一个函数的函数。这种方法不需要修改原始函数的代码,从而可以简单、快捷地添加新的功能。 Python 语言中装饰器通过标准函数来实现,通过装饰器可以让函数具有更灵活的功能,而不需要修改函数本身的代码。
1.1 装饰器的作用
装饰器的作用十分重要,可以让我们非常简单地修改函数,从而实现一些原来不可能或难以实现的功能。装饰器常见的作用有:
维护代码的一致性和可读性;
代码复用;
函数扩展功能;
函数调试功能;
函数执行权限检查。
2. 装饰器的实现
在 Python 语言中,装饰器是通过包装函数来实现的。大多数情况下,装饰器函数的定义方式是相似的,格式为:
def decorator(func):
def wrapper(*args, **kwargs):
# 处理代码
return func(*args, **kwargs)
return wrapper
上面代码中,装饰器函数 decorator 采用函数 func 作为其唯一参数,返回值为内嵌函数 wrapper。内嵌函数 wrapper 的作用是对传入函数 func 进行包装,以实现添加新功能。
先来看一个简单的装饰器的例子:
def my_decorator(func):
def wrapper():
print("Before the function is called.")
func()
print("After the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出结果为:
Before the function is called.
Hello!
After the function is called.
上面的代码中,函数 my_decorator 定义了一个装饰器,函数 wrapper 对传入的函数 func 进行了包装处理。使用装饰器 @my_decorator 修饰 say_hello 函数。最后运行 say_hello 函数,输出结果为装饰器添加的内容。
2.1 装饰器的使用方法
在 Python 中使用装饰器很简单,只需要在被修饰函数的定义前加上 @decorator 即可。使用装饰器的语法格式如下:
@decorator
def function_name():
# 函数体
这种语法相对于传统的装饰函数调用的语法更加直观和方便,同时也使得代码更加简洁美观。
2.2 装饰器的嵌套
装饰器也可以进行嵌套组合使用,以达到更加灵活的效果。示例如下:
def my_decorator1(func):
def wrapper():
print("Before 1.")
func()
print("After 1.")
return wrapper
def my_decorator2(func):
def wrapper():
print("Before 2.")
func()
print("After 2.")
return wrapper
@my_decorator1
@my_decorator2
def say_hello():
print("Hello!")
say_hello()
在上面的代码中,创建了两个装饰器函数 my_decorator1 和 my_decorator2。在被修饰函数 say_hello 上先使用了 my_decorator2 装饰,再在其外层使用了 my_decorator1 装饰。执行 say_hello 时,一个函数在被多次装饰后,将按照从外到内的顺序执行内嵌函数,输出结果如下:
Before 1.
Before 2.
Hello!
After 2.
After 1.
3. 经典应用示范
3.1 日志记录
在使用 Python 进行开发时,我们经常需要将一些操作记录在日志中,以便对日后的调试和维护等工作。通过装饰器的方式,我们可以实现对代码的改动最小化。
import logging
def log_decorator(func):
def wrapper(*args, **kwargs):
logging.warning(f"{func.__name__} is executing")
return func(*args, **kwargs)
return wrapper
@log_decorator
def sum_of_numbers(a, b):
return a + b
print(sum_of_numbers(1, 5))
上面的代码中,定义了一个 log_decorator 装饰器,该装饰器用于记录函数执行情况。在被修饰函数 sum_of_numbers 上使用 @log_decorator,将 sum_of_numbers 传入 log_decorator 中作为其参数。执行 sum_of_numbers 时,输出以下内容:
WARNING:root:sum_of_numbers is executing
6
3.2 计时器
有时候,我们需要知道一个函数执行所需的时间,这时候就需要使用计时器,计时器可以对函数的执行时间进行计时。下面我们将编写一个计时器装饰器:
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
exe_time = end_time - start_time
print(f"{func.__name__} executed in {exe_time} seconds")
return result
return wrapper
@timer_decorator
def big_sum(a, b):
sum_result = 0
for i in range(a, b):
sum_result += i
return sum_result
print(big_sum(1, 1000000))
在代码中,创建了一个计时器装饰器 timer_decorator,该装饰器用于计算函数执行时间。在被修饰的函数 big_sum 上加上 @timer_decorator,最后输出函数执行结果和执行时间,输出结果如下:
499999500000
big_sum executed in 0.0403289794921875 seconds
4. 装饰器的注意事项
虽然装饰器是 Python 的高级语言特性之一,但使用装饰器也要注意一些细节。
4.1 保留原函数信息
在使用装饰器的时候,如果不特别处理,装饰器会丢失原函数的信息,如函数名、函数参数等。
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} function")
return func(*args, **kwargs)
return wrapper
@logger
def some_function(a, b):
return a + b
print(some_function.__name__)
print(some_function.__doc__)
print(some_function.__annotations__)
print(some_function.__globals__)
输出结果如下:
wrapper
None
{}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
# 此处省略... }
使用 functools 模块修饰原函数信息:
解决装饰器丢失原函数信息的问题,Python 中提供了 functools 模块,可以通过该模块的 wraps 方法来修饰装饰器。指定了该参数后,被修饰函数的元信息将不再丢失。
import functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} function")
return func(*args, **kwargs)
return wrapper
@logger
def some_function(a, b):
return a + b
print(some_function.__name__)
print(some_function.__doc__)
print(some_function.__annotations__)
print(some_function.__globals__)
输出结果如下:
some_function
None
{}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__':
# 此处省略... }
4.2 装饰器顺序
在 Python 中,装饰器的执行顺序是按照从下往上,从内到外的顺序执行的。也就是说,装饰器的执行顺序与书写顺序是相反的,这一点在使用装饰器时一定要注意。
def A(fn):
print('start A')
def wrapper():
print('A')
fn()
print('end A')
return wrapper
def B(fn):
print('start B')
def wrapper():
print('B')
fn()
print('end B')
return wrapper
@A
@B
def test():
print('test')
test()
输出结果如下:
start B
start A
A
B
test
end B
end A
从输出结果中可以看出,由于装饰器的执行顺序为从下往上执行,因此在执行 test 函数时,在 @A 装饰器前面的 @B 装饰器先执行,接着是 @A 装饰器执行,因此最终的输出结果为包含了 @A 和 @B 装饰器的函数调用。
5. 小结
Python 装饰器是一种函数编程的高级技巧,通过与装饰器结合使用,可以使代码更加灵活、优雅、易于维护。本文介绍了 Python 装饰器的概念、实现方式、常规应用和注意事项。仅介绍了常规的装饰器应用,Python 装饰器的用法之多大大超过此处所示,混合使用的效果更不可限量。装饰器技术对于 Python 编程来说,是一种不可替代的高级技巧。