python笔记·函数装饰器(decorators)(代码片段)

bluishglc bluishglc     2022-12-16     535

关键词:

Python的装饰器语法与面向切面编程(AOP)的设计意图是高度一致的,多应用于日志、事务处理等具有“横切”性质编程场合,与Java中的动态代理以及设计模式中的代理模式类似(与装饰器模式有相似之处,但有差异),可以认为是Python在语言级别上内置实现了代理模式或面向切面编程模式,也属于一种语法糖。

考虑这样一个普通的函数:

def my_fun():
    print("This is a function")

如果我们想为这个函数,也包括所有其他函数添加一个自动日志的功能,以便记录一下函数调用前后的时间,这是一个典型的“横切”场景,实际上,更加具有实际应用价值的一个案例是:在方法调用前启动一个事务,在方法执行完毕后提交一个事务,此类是更加典型的案例。这时我们为了方便,以日志功能来举例。

为了能实现这个通用的,具有横切功能的函数,我们需要引入高级函数特性,具体实现如下:

def log(func):
    def wrap(*args, **kw):
    print('before call %s():' % func.__name__)
        result = func(*args, **kw)
        print('after call %s():' % func.__name__)
        return result
    return wrap

首先,我们要注意一下这个log的函数的整体“框架”:它先在函数体内定义了一个子函数warp,然后log函数最终返回的其实是wrap函数。wrap函数是实现包裹逻辑的地方。之所以要嵌套声明一个wrap函数,而不是在log中直接实现包裹逻辑的主要原因在于:包裹的逻辑(也就是打日志的代码)不是在“包裹”时执行,而是必须要“推迟”到目标函数调用时“伴随”执行,这就要求从函数完成包裹到函数调用前要有一个“中间载体”,其即包含了中间逻辑,同时在调用行为上还要与目标函数无异,实际上,此时的“中间载体”在Java等语言中就是一个“代理类”(动态代理或通过cglib修改字节码得到代理类),而在Python中,以一个函数形式(就是这里的wrapp)包裹是一种更轻量和边界方法,这就是为什么不能在log中直接实现包裹逻辑,而是要下放到一个子的包裹函数,并反回该函数的原因。接下来,我们就看一下最后的执行效果:

# 完成包裹逻辑定义
my_fun = log(my_fun)
# 调用目标函数的同时也执行了包裹逻辑
# 此时的my_fun已经不是开始时定义的那个my_fun了,而是wrapper
my_fun()

至此,“朴素版”的装饰器函数log就算实现完成了,由于类似的装饰场景普遍存在,Python在语法层面上提升了对这一模式的支持力度,使用了@wrap_fucniton_name的语法来简化了包裹函数和目标函数之间的粘合操作。所以上述代码可以简化为:

def log(func):
    def wrap(*args, **kw):
        print('before call %s():' % func.__name__)
        result = func(*args, **kw)
        print('after call %s():' % func.__name__)
        return result
    return wrap

@log
def my_fun():
    print("This is a function")

my_fun()

我们在my_fun方法前添加@log,就等同于执行了my_fun = log(my_fun)这一操作,也就是自动完成了被包裹函数和包裹函数之间的“注册/关联”操作,这样无疑是更加简洁和优雅的。

此时Python的函数装饰器机制基本就介绍完了,但是,通常人们会因为这样一个小问题导致一些麻烦,就是此时的my_fun已经不是开始时定义的那个my_fun了,因为此时使用print(my_fun.__name__)得到的函数名是warp而不是my_fun,这会导致在执行时需要判断函数名的程序(类似if xxx.__name__ == 'xxx'这样的代码)执行失败,虽然我们确实可以在包裹函数函数中手动修正命名上的偏差,类似wrapper.__name__ = func.__name__这样,但是Python中有更好的更优雅的解决方案functools.wraps:

def log(func):
    @functools.wraps(func)
    def wrap(*args, **kw):
        print('before call %s():' % func.__name__)
        result = func(*args, **kw)
        print('after call %s():' % func.__name__)
        return result
    return wrap

@log
def my_fun():
    print("This is a function")

my_fun()

print(my_fun.__name__)

由此可以看出,使用了@functools.wraps(func)后,wrap函数的__name__属性被自动修正了,这也是为什么@functools.wraps(func)需要func参数的原因,因为它需要读取目标函数的一些属性并根据它们修正warp函数的属性。

全部输出如下:

before call my_fun():
This is a function
after call my_fun():
my_fun

参考:

https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
https://www.runoob.com/w3cnote/python-func-decorators.html

python笔记:装饰器(代码片段)

1装饰器介绍    如果有一批函数都是需要前置或者后置的工作,那么我们可以统一使用装饰器进行修饰。  1.1不用装饰器的版本  defdecorator(fn,name):print("I'min",name)returnfn(name)defouter_fn(name):print("I'moutof",name)... 查看详情

python进阶装饰器(decorator)(代码片段)

装饰器(Decorator)装饰器1.装饰器的定义2.装饰器的示例代码3.装饰器的语法糖写法4.小结装饰器的使用1.装饰器的使用场景2.装饰器实现已有函数执行时间的统计2.小结通用装饰器的使用1.装饰带有参数的函数2.装饰带有返... 查看详情

js装饰器decorator

装饰器Decorator参考:阮一峰ES6装饰器是一种函数,写成@函数名它可以放在类和类方法的定义前面。与Java的注解一样,都是用于增强类的方法的。类的装饰@AddAgeclassPerson{}//这里的target就是类Person,参数只能有一个functionAddAge(target){co... 查看详情

typescript学习笔记:装饰器(decorators)

装饰器简介装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变。若要启用实验性的装饰器特性,你必须在命... 查看详情

js装饰器@decorator学习笔记(代码片段)

文章目录一、注解的基本用法二、扩展类一、注解的基本用法/***注解执行是有顺序的,顺序如下*1.先执行带参数的注解*2.再按照出现顺序执行属性或方法的注解*3.最后按照栈(先书写后执行)类的注解*/@fn@fn3... 查看详情

js装饰器@decorator学习笔记(代码片段)

文章目录一、注解的基本用法二、扩展类一、注解的基本用法/***注解执行是有顺序的,顺序如下*1.先执行带参数的注解*2.再按照出现顺序执行属性或方法的注解*3.最后按照栈(先书写后执行)类的注解*/@fn@fn3... 查看详情

20181130(装饰器补充,叠加多个装饰器,有参装饰器,三元表达式,生成式,匿名函数,内置函数)

 一、装饰器的补充1、函数属性的传递Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这... 查看详情

类 Python C++ 装饰器

】类PythonC++装饰器【英文标题】:Python-likeC++decorators【发布时间】:2015-08-2103:59:37【问题描述】:有没有办法像python风格一样在C++中装饰函数或方法?@decoratordefdecorated(self,*args,**kwargs):pass以宏为例:DECORATE(decorator_method)intdecorated(... 查看详情

flask框架-decorator装饰器

调用包:fromfunctoolsimportwraps装饰器其实就是一个函数:参数是一个函数,返回值是一个函数1.装饰器使用是通过@符号,在函数的上面2.装饰器中定义的函数,要使用*args,**kwargs两对兄弟组合,并且在这个函数中执行原始函数的时... 查看详情

「低门槛手把手」python装饰器(decorators)原理说明

参考技术A本文目的是由浅入深地介绍python装饰器原理装饰器(Decorators)是Python的一个重要部分其功能是,在不修改原函数(类)定义代码的情况下,增加新的功能为了理解和实现装饰器,我们先引入2个核心操作:在这个例子中,函... 查看详情

python装饰器decorators(代码片段)

...、类装饰器六、装饰器顺序一、功能装饰器本质上是一个Python函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,... 查看详情

python函数装饰器详解(代码片段)

基础:函数装饰器的表现方式假如你已经定义了一个函数funcA(),在准备定义函数funcB()的时候,如果写成下面的格式:@funcAdeffuncB():...表示用函数funcA()装饰函数funcB()。当然,也可以认为是funcA包装函数funcB。它等价于:deffuncB():...... 查看详情

python学习笔记:装饰器生成器内置函数json

这周学习了装饰器和生成器,写下博客,记录一下装饰器和生成器相关的内容。一、装饰器装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,... 查看详情

python学习笔记:装饰器生成器内置函数json

这周学习了装饰器和生成器,写下博客,记录一下装饰器和生成器相关的内容。一、装饰器装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,... 查看详情

如何使用 python-decorator 包来装饰类方法?

】如何使用python-decorator包来装饰类方法?【英文标题】:HowdoIusethepython-decoratorpackagetodecorateclassmethods?【发布时间】:2013-09-0518:57:40【问题描述】:我有一个装饰器,我想用它来装饰类方法。在下面的示例中,@mydec装饰器本身可... 查看详情

python装饰器的另类用法(代码片段)

之前有比较系统介绍过Python的装饰器,本文算是一个补充。今天我们一起探讨一下装饰器的另类用法。语法回顾开始之前我们再将Python装饰器的语法回顾一下。@decoratedeff(...):pass等同于:deff(...):passf=decorate(f)@语法的好处在于:相... 查看详情

python学习笔记:装饰器生成器内置函数json

一、装饰器装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面... 查看详情

检查函数是不是被称为装饰器

】检查函数是不是被称为装饰器【英文标题】:Checkifafunctionwascalledasadecorator检查函数是否被称为装饰器【发布时间】:2019-02-1100:04:05【问题描述】:在以下最小示例中,decorate被调用了两次。首先使用@decorate,其次是普通函数调... 查看详情