一篇通俗易懂的Python 装饰器?

一篇通俗易懂的Python 装饰器?

  • 如果认真看完这篇文章还不理解装饰器的话,只能说你没有入门.讲 Python 装饰器前,我想先举个例子,但跟装饰器这个话题让大家通俗易懂.每个人都有的内衣主要功能是用来遮羞,但是到了秋冬季天它没法为我们防风御寒,那怎么办呢?
  • 我们想到的一个办法就是把内衣改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内衣被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了.于是聪明的人们发明长裤,在不影响内裤功能的前提下,直接把长裤套在了内裤外面,这样内衣还是长裤,有了长裤后宝宝再也不冷了.装饰器就像我们这里说的长裤,在不影响长裤作用的前提下,额外给我们的身子提供了保暖防寒的功效.谈装饰器前,还要先要明白一件事,Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如:
scss 复制代码
def foo():
    print("foo")

def bar(func):
    func()

bar(foo)
  • 装饰器本质上是一个 Python 函数.它的作用在于允许您在不需要修改其他函数代码的情况下为这些函数添加额外功能。值得记住的是,装饰器本身也是一个函数,并且其返回值也是一个函数。.它经常用于有一些需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计. 装饰器的作用是将与函数功能本身无关的重复代码抽离到装饰器中,以便继续重用。简而言之,装饰器允许为已存在的函数添加额外功能。以下是一个简单例子,实际情况可能更为复杂:
python 复制代码
def my_decorator(func):
    def wrapper():
        print("Do something before the function is called")
        func()
        print("Do something after the function is called")
    return wrapper

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

say_hello()
  • 在这个示例中,my_decorator 是一个装饰器函数,它通过 wrapper 函数包裹原始函数 say_hello,从而在调用 say_hello 时添加额外的功能
  • 现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码:
python 复制代码
import logging

def log_function(func):
    def wrapper():
        print("Executing function:", func.__name__)
        logging.info(f"{func.__name__} is running")
        func()
    return wrapper

@log_function
def foo():
    print('I am foo')

foo()
  • 在这个优化后的代码中,我们定义了一个装饰器 log_function,它会记录函数执行前后的信息。通过将 @log_function 应用到 foo 函数上,我们使得在调用 foo() 时会先输出函数执行信息,然后再执行原始函数体内的内容,并且记录日志信息。
python 复制代码
import logging

def use_logging(func):
    def wrapper():
        logging.warning("%s is running" % func.__name__)
        func()
    return wrapper

@use_logging
def foo():
    print('I am foo')

foo()
  • 在这个优化后的代码中,我们定义了装饰器 use_logging,它包裹原始函数 foo。装饰器内部的 wrapper 函数用于记录日志信息,并最终调用目标函数 func()。通过将 @use_logging 应用到 foo 函数上,使得在执行 foo() 时会先输出日志信息,然后再执行原始函数体内的内容。

简单装饰器

python 复制代码
import logging

def use_logging(func):
    def wrapper():
        logging.warning("%s is running" % func.__name__)
        return func()
    return wrapper

@use_logging
def foo():
    print('I am foo')

foo()
  • 在这个优化后的代码中,我们使用装饰器 @use_logging 来包裹函数 foo,避免了显式地将 foo 赋值给 use_logging(foo) 。这样使得代码更加简洁、易读,同时实现了对 foo 函数的日志记录功能增强。

@ 语法糖

  • 当你接触 Python 有一段时间了的话,你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作.
python 复制代码
import logging

def use_logging(func):
    def wrapper():
        logging.warning("%s is running" % func.__name__)
        return func()
    return wrapper

@use_logging
def foo():
    print("I am foo")

foo()
  • 在这个优化后的代码中,通过使用 @use_logging 装饰器语法,可以直接在定义函数时为其添加额外功能,而无需显式调用装饰器。这种方式提高了程序的可重复利用性和可读性,同时展示了 Python 函数作为一等对象的特性,使得装饰器在 Python 中应用非常方便且灵活。

*args、**kwargs

python 复制代码
import logging

def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.warning("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

@use_logging
def foo(name, age=None, height=None):
    print("I am %s, age %s, height %s" % (name, age, height))

foo("Alice", age=30, height=170)
  • 在这个代码中,装饰器 use_logging中的wrapper 函数使用*args和**kwargs 来分别表示接收任意数量位置参数和关键字参数。这样使得装饰器可以适用于不同函数签名的情况,保持了对被装饰函数的完整参数传递,并对其添加额外功能,同时不会影响原始函数的业务逻辑。

带参数的装饰器

python 复制代码
import logging

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warning("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("I am %s" % name)

foo()
  • 在这个优化后的代码中,装饰器 use_logging 现在接受一个参数 level,并返回一个带有参数的装饰器 decorator。当使用 @use_logging(level="warn") 时,实际上是调用了 decorator 函数,并将参数传递到内部环境中。这种方式使得我们可以根据不同的需求为函数动态添加不同的功能,提高了装饰器的灵活性。

类装饰器

python 复制代码
import functools

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('Class decorator running')
        self._func()
        print('Class decorator ending')

@Foo
def bar():
    print('Bar')

bar()

from functools import wraps

def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__)      # 输出 'f'
        print(func.__doc__)       # 输出 'does some math'
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

f(5)
  • 在这个代码中,我们展示了类装饰器和函数装饰器的用法。同时,使用 functools.wraps 装饰器确保装饰器内部的函数(如 with_logging)保留了原始函数的元信息,如 namedoc。这样就可以避免原始函数信息丢失的问题,提高了装饰器的可靠性和易用性。

装饰器顺序

python 复制代码
def a(func):
    print("Decorator A")

    def wrapper():
        print("Decorator A - Before")
        func()
        print("Decorator A - After")

    return wrapper

def b(func):
    print("Decorator B")

    def wrapper():
        print("Decorator B - Before")
        func()
        print("Decorator B - After")

    return wrapper

def c(func):
    print("Decorator C")

    def wrapper():
        print("Decorator C - Before")
        func()
        print("Decorator C - After")
    return wrapper
@c
@b
@a
def f():
    print("Original function")
f()
  • 在这个代码示例中,展示了如何使用多个装饰器来装饰同一个函数f.根据装饰器的嵌套顺序,最先调用最里层的装饰器,最后调用最外层的装饰器。这种装饰器链式调用的方式可以灵活地对函数进行增强和修改,提高代码的可维护性和可读性.
  • 感谢大家的关注和支持!想了解更多Python编程精彩知识内容,请关注我的 微信公众号:python小胡子,有最新最前沿的的python知识和人工智能AI与大家共享,同时,如果你觉得这篇文章对你有帮助,不妨点个赞,并点击关注.动动你发财的手,万分感谢!!!
相关推荐
蹉跎x4 分钟前
力扣104. 二叉树的最大深度
算法·leetcode·职场和发展
gaogao_jack6 分钟前
[Leetcode小记] 3233. 统计不是特殊数字的数字数量
java·算法·leetcode
沃和莱特14 分钟前
C++中类的继承
数据库·c++·编程·c·指针·友元函数
zzzhpzhpzzz20 分钟前
设计模式——解释器模式
算法·设计模式·解释器模式
FF在路上30 分钟前
MySQL数据库-视图的介绍和使用
数据库·sql·mysql
一只鸡某30 分钟前
实习冲刺第二十九天
数据结构·c++·算法·leetcode
ZZZ_O^O1 小时前
【贪心算法入门第一题——860.柠檬水找零】
学习·算法·leetcode·贪心算法
Easy数模1 小时前
竞赛思享会 | 2024年第十届数维杯国际数学建模挑战赛D题【代码+演示】
python·算法·数学建模
数字扫地僧2 小时前
如何使用MySQL实现多租户架构:设计与实现全解析
数据库·mysql·架构
乄bluefox2 小时前
SpringBoot中使用Sharding-JDBC实战(实战+版本兼容+Bug解决)
java·数据库·spring boot·redis·后端·缓存·bug