python 装饰器详解

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 编程来说,是一种不可替代的高级技巧。

后端开发标签