Python中的装饰器是什么?

装饰器(Decorator)是 Python 中一个非常实用的功能,它可以让你在不改变原有代码功能的前提下,为函数或类添加新的功能。这就像在装扮你的房间时,你可以用装饰品来改变房间的外观和风格,而不需要改变房间的内部结构。

装饰器又名(函数/类)装饰器,顾名思义,就是装饰(函数/类)的,用来增强(函数/类)的功能。

装饰器运行的顺序

被装饰函数(inner)在哪里执行要看你在哪里调用(只会执行一次)。最先执行的是outer,当outer运行完成后,在return的地方执行了wrapper。

下面是一个简单的例子,用装饰器来记录函数执行时间:

import time  
  
def timer(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:.6f} seconds to execute.")  
        return result  
    return wrapper  
  
@timer  
def my_func():  
    time.sleep(1)  
  
my_func()  # 输出:Function my_func took 1.001149 seconds to execute.

在这个例子中,我们定义了一个装饰器 timer,它接收一个函数作为参数,并返回一个新的函数 wrapper。新函数 wrapper 记录了原函数的执行时间,并在执行原函数后输出执行时间。最后,我们使用 @timer 语法将装饰器应用到函数 my_func 上,这样每次调用 my_func 时就会自动记录执行时间。

装饰器的作用非常广泛,可以用它来实现缓存、日志记录、权限校验、性能分析等功能。它可以让你的代码更加简洁、优雅和可维护。
除了上面的例子,下面还有一些常见的装饰器用法和示例:

类型提示

def typed(func):  
    def wrapper(*args, **kwargs):  
        print(f"Calling {func.__name__} with {args} and {kwargs}")  
        return func(*args, **kwargs)  
    return wrapper  
  
@typed  
def greet(name: str, age: int) -> None:  
    print(f"Hello, my name is {name} and I'm {age} years old.")  
  
greet("Alice", 25)  # 输出:Calling greet with ('Alice', 25) and {}

在这个例子中,我们定义了一个装饰器 typed,它接收一个函数作为参数,并返回一个新的函数 wrapper。新函数 wrapper 打印传入的参数,并调用原函数。原函数 greet 带有类型提示,它接受一个字符串类型的 name 和一个整数类型的 age,并返回 None。最后,我们使用 @typed 语法将装饰器应用到函数 greet 上,这样每次调用 greet 时就会打印传入的参数。

权限校验

def check_permission(func):  
    def wrapper(*args, **kwargs):  
        if not current_user.can_access(func.__name__):  
            raise PermissionDeniedException()  
        return func(*args, **kwargs)  
    return wrapper  
  
@check_permission  
def delete_post(post_id: int) -> None:  
    database.delete_post(post_id)

在这个例子中,我们定义了一个装饰器 check_permission,它接收一个函数作为参数,并返回一个新的函数 wrapper。新函数 wrapper 校验当前用户是否有执行原函数的权限,如果没有则抛出异常。原函数 delete_post 接收一个整数类型的 post_id,并从数据库中删除对应的帖子。最后,我们使用 @check_permission 语法将装饰器应用到函数 delete_post 上,这样每次调用 delete_post 时就会校验权限。
缓存

from functools import wraps  
  
def cached(func):  
    cache = {}  
    @wraps(func)  
    def wrapper(*args, **kwargs):  
        if args not in cache:  
            cache[args] = func(*args, **kwargs)  
        return cache[args]  
    return wrapper  
  
@cached  
def factorial(n):  
    if n == 0:  
        return 1  
    else:  
        return n * factorial(n-1)  
  
print(factorial(5))  # 输出:120,计算结果缓存在cache中,第二次调用时直接返回缓存结果

在这个例子中,我们使用了 functools.wraps 来保留原函数的元数据,定义了一个装饰器 cached,它接收一个函数作为参数,并返回一个新的函数 wrapper。新函数 wrapper 使用一个字典 cache 来缓存计算结果,如果传入的参数不在缓存中,则调用原函数计算结果并缓存起来,否则直接返回缓存结果。原函数 factorial 计算阶乘,使用装饰器 @cached 将它应用到函数上,这样每次调用 factorial 时都会使用缓存结果,避免重复计算。

日志记录

def log(func):  
    @wraps(func)  
    def wrapper(*args, **kwargs):  
        print(f"Calling {func.__name__} with {args} and {kwargs}")  
        result = func(*args, **kwargs)  
        print(f"Result: {result}")  
        return result  
    return wrapper

在Python中,我们将会在很多地方看到装饰器的应用场景,比如Python中的 @classmethod、@staticmethod、上面例子中使用的 @functools.wraps(func) 等都使用到了装饰器,另外很多第三方库中也会有大量的装饰器应用。

通过使用装饰器,我们不仅让其他函数在不做任何代码改动的情况下增加额外的功能,同时减少了很多重复代码,让代码更加Pythonic。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值