Python 装饰器

发布于 2024-10-30 16:23:17 字数 4501 浏览 6 评论 0

装饰器在不修改源代码块的情况下,增加代码的功能,有助于代码的精简,抽象出公共的辅助模块,便于维护代码

入门

入门主要以 python 之禅的教程,谈装饰器前,还要先要明白一件事,Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数

def foo():
    print("foo")

def bar(func):
    func()

bar(foo)

装饰器本质上是一个 Python 函数或类 ,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于 有切面需求的场景 ,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

# 打印日志为例子
def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()

def foo():
    print('i am foo')
# 打印日志的功能可以实现 但是破坏了原来的代码结构 变成每次调用 foo 都要调用 use_logging(foo)
use_logging(foo)
def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()   # 把 foo 当做参数传递进来时,执行 func() 就相当于执行 foo()
    return wrapper

def foo():
    print('i am foo')

foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapper
foo()

use_logging 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 func 包裹在其中,看起来像 foo 被 use_logging 装饰了一样,use_logging 返回的也是一个函数,这个函数的名字叫 wrapper。在这个例子中,函数进入和退出时,被称为一个横切面,这种编程方式被称为 面向切面的编程

@语法糖

此时会发现还是要为程序多添加一句 foo = use_logging(foo) ,python 提供了 @ 的语法糖,用于省略 foo = use_logging(foo) 这样的赋值功能

def use_logging(func):
    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()
    return wrapper
@use_logging  # 实现了 foo = use_logging(foo) 的功能
def foo():
    print('i am foo')
foo()

装饰有参数的函数

# 相当于 foo = use_logging(foo) 返回的是一个 wrapper 函数, 然后调用 wrapper(name, age=None, height=None)
def use_logging(func):
    def wrapper(*args, **kwargs):
        # args 是一个数组,kwargs 一个字典
        logging.warn("%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))

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器。装饰器的语法允许我们在调用时,提供其它参数,比如 @decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper
    return decorator
@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)
foo()

类装饰器

相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性 可继承性(详见 Python 进阶-装饰器 ) 等优点。使用类装饰器主要依靠类的 __call__ 方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

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

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')
@Foo
def bar():
    print ('bar')
bar()

functools.wraps 找回被装饰函数的元信息

from functools import wraps
def logged(func):
    @wraps(func)  # with_logging = wraps(with_logging)
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'f',如果没有 @wraps(func) 返回的是 with_logging
        print func.__doc__       # 输出 'does some math',如果没有 @wraps(func) 返回的是 None
        return func(*args, **kwargs)
    return with_logging
@logged
def f(x):
   """does some math"""
   return x + x * x
# 这个相当于 f = wraps(logged(f))

多个装饰器

@a
@b
@c
def f ():
    pass

执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于 f = a(b(c(f)))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

旧时浪漫

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

安静被遗忘

文章 0 评论 0

喔爱吃橙子

文章 0 评论 0

草莓味的萝莉

文章 0 评论 0

梦里兽

文章 0 评论 0

mb_83J3Cyxa

文章 0 评论 0

时间海

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文