编程学习网 > 编程语言 > Python > Python装饰器:实现代码精简的利器!
2024
03-30

Python装饰器:实现代码精简的利器!


今天,我们来探索 Python 装饰器的强大功能,学习如何通过使用装饰器来减少代码量,提高效率与可读性。


什么是装饰器
装饰器(decorator)是 Python 中一种强大的特性,它允许你改变函数或类的行为,而无需修改其源代码。装饰器本质上是一个函数,它接收另一个函数作为参数,并返回一个新的函数,这个新函数会包裹原始函数。通过这种方式,你可以在不修改原始函数的情况下,为其添加一些额外的功能或逻辑。

例如,假设你有一个函数,用于在控制台打印一条消息:

def hello():
    print("Hello, world!")
现在,假设你想测量这个函数的执行时间。你可能会这样做:

import time

def hello():
    start = time.time()
    
    print("Hello, world!")
    
    end = time.time()
    print(f"执行时间: {end - start} 秒")
但考虑到测量执行时间的功能很可能会复用,我们最好将它独立封装成一个函数,使用 time 模块来计算执行时间,然后调用原始函数:

import time

def measure_time(func):
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(f"执行时间: {end - start} 秒")
    return wrapper
注意,measure_time 函数返回了另一个名为wrapper的函数,这是原始函数的修改版本。wrapper 函数做了两件事:它记录了执行的开始和结束时间,并调用了原始函数。

现在,要使用这个函数,只需要:

hello = measure_time(hello)
hello()
这将输出类似于这样的内容:

Hello, world!
执行时间: 0.000123456789 秒
通过这种方式,你无需修改hello函数的代码,就为其添加了测量执行时间的新功能。然而,有一种更优雅和简洁的方法来实现这一点,那就是使用装饰器。装饰器是一种语法糖,允许你使用@符号将一个函数应用到另一个函数上。例如,我们可以像这样重写前面的代码:

@measure_time
def hello():
    print("Hello, world!")
hello()
这将产生与之前相同的输出,但代码量大大减少。@measure_time这一行等同于hello = measure_time(hello),但它看起来更清晰,更易于阅读。

为什么要用装饰器
使用装饰器的原因有很多,例如:

代码可复用。例如,如果你有许多需要测量执行时间的函数,你可以简单地为它们都应用相同的装饰器,而不是一遍又一遍地编写相同的代码。
遵循单一职责的原则。例如,如果你有一个执行复杂计算功能的函数,你可以使用装饰器来处理日志记录、错误处理、缓存或验证输入和输出,而不必弄乱函数的主要逻辑。
扩展功能的同时无需修改源代码。例如,如果你正在使用一个第三方库的类或函数,但你想为它们添加一些额外的功能或行为,你可以使用装饰器来包装它们,并根据你的需要进行定制。
装饰器示例
Python 中有许多内置装饰器,如@staticmethod、@classmethod、@property、@functools.lru_cache、@functools.singledispatch等。你也可以创建自己的自定义装饰器来满足各种需求。以下是一些可以有效精简代码的自定义装饰器示例:

@timer装饰器
这个装饰器与我们上面看到的@measure_time装饰器类似,但可以应用于任意数量参数的函数。它还使用functools.wraps装饰器来保留原始函数的名称和文档字符串(docstring)。代码如下:

import time
from functools import wraps
def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__}的执行时间: {end - start} 秒")
        return result
    return wrapper
现在,你可以使用这个装饰器来测量任何函数的执行时间,例如:

@timer
def factorial(n):
    """返回n的阶乘"""
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n - 1)
@timer
def fibonacci(n):
    """返回第n个斐波那契数"""
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))
这将输出类似于这样的内容:

factorial的执行时间: 1.1920928955078125e-06 秒
3628800
fibonacci的执行时间: 0.000123456789 秒
55
通过这种方式,@timer装饰器为factorial和fibonacci函数添加了测量执行时间的新功能。

@debug装饰器
这个装饰器可用于调试目的,它支持输出它所装饰的函数的名称、参数和返回值。并使用functools.wraps装饰器来保留原始函数的名称和文档字符串。代码如下:

from functools import 
def debug(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__},参数: {args},关键字参数: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 返回: {result}")
        return result
    return wrapper
现在,你可以使用这个装饰器来调试任何函数,例如:

@debug
def add(x, y):
    """返回x和y的和"""
    return x + y
@debug
def greet(name, message="Hello"):
    """返回带有名字的问候语"""
    return f"{message}, {name}!"
print(add(2, 3))
print(greet("Alice"))
print(greet("Bob", message="Hi"))
这将输出类似于这样的内容:

调用 add,参数: (2, 3),关键字参数: {}
add 返回: 5
5
调用 greet,参数: ('Alice',),关键字参数: {}
greet 返回: Hello, Alice!
Hello, Alice!
调用 greet,参数: ('Bob',),关键字参数: {'message': 'Hi'}
greet 返回: Hi, Bob!
Hi, Bob!
通过这种方式,@debug装饰器为add和greet函数添加了调试功能,能够清晰地显示函数调用时的参数和返回值。

@memoize装饰器
这个装饰器对于优化函数性能非常有用(尤其是用了递归、或是需要大量计算的函数),因为它缓存了之前调用的结果,并在再次传递相同的参数时返回这些结果。同样使用了functools.wraps装饰器来保留原始函数的名称和文档字符串。代码如下:

from functools import wraps
def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args in cache:
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result
    return wrapper
现在,你可以使用这个装饰器来缓存任何函数的结果,例如:

@memoize
def factorial(n):
    """返回n的阶乘"""
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n - 1)
@memoize
def fibonacci(n):
    """返回第n个斐波那契数"""
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))
这将输出与之前相同的结果,但执行时间会快得多,因为执行结果做了缓存。

通过这种方式,@memoize装饰器为factorial和fibonacci函数添加了缓存结果的功能,大大提高了这些函数的执行效率。

结语

Python 装饰器是一种强大而优雅的特性,能够在不修改源代码的前提下,改变函数或类的行为。它们可以帮助你减少代码量,提高代码的可读性,复用性,降低耦合性,以及扩展现有代码的功能。

以上就是Python装饰器:实现代码精简的利器!的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取