0
点赞
收藏
分享

微信扫一扫

[3]Python高级特性-【3】装饰器

概述

在Python中,装饰器是一种特殊的函数,它可以修改其他函数的行为。装饰器本身就是一个函数,它接受一个函数作为参数,并返回一个新的函数,这个新的函数通常用于包装原始函数,添加一些额外的功能或者修改原始函数的行为。装饰器的使用可以使代码更加简洁、易读和可维护。

装饰器的基本使用

下面通过一个简单的例子来介绍装饰器的基本使用:

def my_decorator(func):
    def wrapper():
        print("Before the function is called.")
        func()
        print("After the function is called.")
    return wrapper

def say_hello():
    print("Hello!")

say_hello = my_decorator(say_hello)
say_hello()

在这个例子中,我们定义了一个装饰器函数my_decorator,它接受一个函数func作为参数,并返回一个新的函数wrapper。wrapper函数在调用原始函数func之前和之后分别打印了一些信息。

我们还定义了一个函数say_hello,它打印了一句问候语。然后我们将say_hello函数传递给my_decorator装饰器函数进行装饰,并将装饰后的函数重新赋值给say_hello。最后,我们调用say_hello函数,输出结果如下:

Before the function is called.
Hello!
After the function is called.

可以看到,在调用say_hello函数时,实际上是先调用了my_decorator函数,然后将返回的wrapper函数作为新的say_hello函数。在调用新的say_hello函数时,会先执行wrapper函数打印"Before the function is called.",然后再执行原始的say_hello函数打印"Hello!",最后再执行wrapper函数打印"After the function is called."。这样我们就成功地通过装饰器修改了say_hello函数的行为。

装饰器的高级用法

除了基本的装饰器用法外,装饰器还有许多高级用法,如接受参数的装饰器、嵌套装饰器等。

接受参数的装饰器

有些情况下,我们希望在装饰器中传递一些参数,以便在装饰器中使用。为了实现这个功能,我们需要在装饰器函数外再套一层函数,用于接收参数并返回装饰器函数。例如:

def my_decorator(param):
    def decorator(func):
        def wrapper():
            print("Before the function is called with parameter:", param)
            func()
            print("After the function is called with parameter:", param)
        return wrapper
    return decorator

@my_decorator("test parameter")
def say_hello():
    print("Hello!")

say_hello()

在这个例子中,我们定义了一个接受参数的装饰器my_decorator,它接受一个参数param,并返回一个装饰器函数decorator。decorator函数接受一个函数func作为参数,并返回一个新的函数wrapper。wrapper函数在调用原始函数func之前和之后分别打印了一些信息,并输出装饰器参数param。

我们使用@语法糖将my_decorator装饰器应用到say_hello函数上,并传递了一个参数"test parameter"。在调用say_hello函数时,实际上是调用了装饰后的wrapper函数,并输出了装饰器参数"test parameter"。

嵌套装饰器

在Python中,我们还可以使用多个装饰器对同一个函数进行装饰,这就是嵌套装饰器。例如:

def decorator1(func):
    def wrapper():
        print("decorator1 before")
        func()
        print("decorator1 after")
    return wrapper

def decorator2(func):
    def wrapper():
        print("decorator2 before")
        func()
        print("decorator2 after")
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("Hello!")

say_hello()

在这个例子中,我们定义了两个装饰器函数decorator1和decorator2,它们都接受一个函数func作为参数,并返回一个新的函数wrapper。wrapper函数在调用原始函数func之前和之后分别打印了一些信息。

我们使用@语法糖将decorator1和decorator2两个装饰器应用到say_hello函数上。在调用say_hello函数时,实际上是先调用了装饰器decorator2返回的新函数,然后再调用装饰器decorator1返回的新函数,最后执行原始函数say_hello打印"Hello!"。

在嵌套装饰器中,装饰器的执行顺序是从下往上,也就是先执行离函数最近的装饰器。在上面的例子中,先执行了decorator2的wrapper函数,然后再执行decorator1的wrapper函数,最后执行了say_hello函数。

除了使用@语法糖应用多个装饰器外,我们还可以手动对函数进行多层装饰:

def decorator1(func):
    def wrapper():
        print("decorator1 before")
        func()
        print("decorator1 after")
    return wrapper

def decorator2(func):
    def wrapper():
        print("decorator2 before")
        func()
        print("decorator2 after")
    return wrapper

def say_hello():
    print("Hello!")

say_hello = decorator1(decorator2(say_hello))

say_hello()

在这个例子中,我们手动将say_hello函数进行两层装饰,先使用decorator2装饰,然后再使用decorator1装饰。在调用say_hello函数时,实际上是先执行了decorator2返回的新函数,然后再执行decorator1返回的新函数,最后执行原始函数say_hello打印"Hello!"。

多层装饰的执行顺序也是从下往上,先执行离函数最近的装饰器。

类装饰器

除了函数装饰器外,Python还支持使用类作为装饰器。类装饰器是指实现了__call__方法的类,这个方法会在使用@语法糖调用装饰器时被调用。

例如,我们可以定义一个计时器类装饰器:

import time

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print(f"{self.func.__name__} took {end - start} seconds")
        return result

@Timer
def countdown(n):
    while n > 0:
        n -= 1

countdown(10000000)

在这个例子中,我们定义了一个计时器类装饰器Timer,它接受一个函数func作为参数,并实现了__call__方法。在__call__方法中,我们记录了函数开始执行的时间,执行函数,并记录函数结束执行的时间,然后打印出函数的执行时间。

使用@语法糖将Timer装饰器应用到countdown函数上,在调用countdown函数时,实际上是调用了Timer类的__call__方法,并记录了函数的执行时间。

多个装饰器的组合

在Python中,我们可以将多个装饰器应用于同一个函数,以实现多个不同的功能。

例如,我们可以定义一个装饰器print_args,用来打印函数的参数列表:

from functools import wraps

def print_args(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Arguments: {args} {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@print_args
@Timer
def countdown(n):
    while n > 0:
        n -= 1

countdown(100000)

在这个例子中,我们在原来的计时器装饰器上再加上一个print_args装饰器,用来打印函数的参数列表。在使用@语法糖将装饰器应用到countdown函数上时,Python会先应用最后一个装饰器,然后是倒数第二个,以此类推。因此,countdown函数实际上被先应用了print_args装饰器,然后是Timer装饰器。

装饰器的应用场景

装饰器可以用于许多不同的场景,下面介绍几个装饰器的应用场景。

  1. 日志记录

装饰器可以用于记录函数的调用情况,包括函数的参数、返回值、调用时间等信息。这在调试和排查问题时非常有用。下面是一个记录函数调用情况的装饰器示例:

import time

def log(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Function {func.__name__} took {end - start} seconds to run.")
        return result
    return wrapper

@log
def my_function():
    time.sleep(1)
    return "Hello world!"

result = my_function()
print(result)

  1. 计时器

装饰器可以用于计算函数的执行时间,这对于优化程序性能非常有用。下面是一个计时器装饰器示例:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Function {func.__name__} took {end - start} seconds to run.")
        return result
    return wrapper

@timer
def my_function():
    time.sleep(1)
    return "Hello world!"

result = my_function()
print(result)

  1. 缓存

装饰器可以用于缓存函数的结果,避免重复计算。下面是一个缓存装饰器示例:

def cache(func):
    cached_results = {}

    def wrapper(*args):
        if args in cached_results:
            return cached_results[args]
        result = func(*args)
        cached_results[args] = result
        return result
    return wrapper

@cache
def fibonacci(n):
    if n in (0, 1):
        return n
    return fibonacci(n-1) + fibonacci(n-2)

result = fibonacci(10)
print(result)

  1. 授权

装饰器可以用于检查用户是否有足够的权限来执行某个操作。下面是一个授权装饰器示例:

def requires_permission(permission):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if permission in current_user.permissions:
                return func(*args, **kwargs)
            else:
                raise Exception("User does not have permission.")
        return wrapper
    return decorator

@requires_permission("admin")
def delete_user(user_id):
    # code here

在上述例子中,requires_permission是一个带参数的装饰器函数,它接收一个字符串参数表示所需的权限。decorator是一个内部函数,它接收一个函数作为参数,并返回一个新的函数wrapper。wrapper函数检查当前用户是否具有所需的权限,如果具有,则调用原始函数;否则,引发异常。

  1. 优化缓存

装饰器也可以用于优化缓存,比如我们要计算一个函数的结果,但是每次调用都需要进行一些复杂的计算,如果这个函数需要经常调用,那么这将会是一个很大的开销。我们可以使用一个装饰器来缓存函数的结果,以便于以后的调用。

以下是一个使用装饰器实现缓存的例子:

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result
    return wrapper

@memoize
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(100))

在上面的代码中,我们定义了一个名为 memoize 的装饰器,这个装饰器将函数的结果缓存起来。然后,我们将 memoize 装饰器应用到 fibonacci 函数上,这样我们就可以缓存 fibonacci 函数的结果了。

  1. 日志记录

装饰器还可以用于记录函数的执行日志,比如我们可以记录函数的执行时间、输入参数以及输出结果等信息,以便于后续的调试和分析。

以下是一个使用装饰器记录日志的例子:

import time

def log_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {(end_time - start_time)*1000:.6f} ms to execute")
        return result
    return wrapper

@log_time
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(10))

在上面的代码中,我们定义了一个名为 log_time 的装饰器,这个装饰器可以记录函数的执行时间。然后,我们将 log_time 装饰器应用到 factorial 函数上,这样每次调用 factorial 函数时,都会自动记录下函数的执行时间。

除了上述常见的装饰器用途,装饰器还可以用于实现AOP(面向切面编程)和函数式编程等高级技术。例如,可以使用装饰器实现函数柯里化、函数组合、函数延迟执行等函数式编程特性,提高程序的可读性和可维护性。

总结

Python中的装饰器是一种用于修改函数或类的行为的特殊函数。它可以在不修改原始函数或类代码的情况下,为其添加新的功能。装饰器的主要作用是对函数或类进行功能增强,常见的装饰器用途包括记录函数运行时间、缓存函数的计算结果、检查函数参数类型和数量、记录函数日志等。在实际应用中,装饰器还可以用于实现AOP和函数式编程等高级技术。

举报

相关推荐

0 条评论