Python中的装饰器

概述

装饰器的概念灵感来源于Java,以特殊字符 @ 开头后跟装饰器的名字
装饰器本质上就是一个函数:
① 它的返回值是一个可调用的函数对象,这个返回的可调用对象默认会隐式的赋值给这个装饰器所装饰的函数对象,返回的函数对象可以是被装饰的函数对象(在装饰器中对该函数的属性做了某些修改等),也可以是一个新的函数对象,装饰的返回值可以是可调用的函数对象,也可以不是,但一般都是可调用的函数对象。
② 它接收它所装饰的函数对象作为它的参数(通过指定函数名传参)

装饰器的主要功能就是: 修改它所装饰的函数的功能

个人理解: 可以把装饰器看作成C语言的一个用于接收回调函数的具有注册功能的函数,被装饰器修饰的函数看作是一个回调函数,在装饰器注册函数中,将被装饰函数的指针作为在注册函数中的另一个回调函数的参数,在这个注册函数中的回调函数中,它的实现是: 为被装饰的函数新增了一些其他功能实现,并执行被装饰的函数,注意在注册函数中确定的回调并未执行,而是作为装饰器的返回值,返回了。

** 以下是对Python的装饰器的理解,使用C代码实现的。**

#include <stdio.h>


//装饰器返回的函数类型
typedef void (*INNER_CALLBACK_FUNC_TYPE)(void *);
//被装饰函数的函数类型
typedef void (*DECTORATORED_FUNC_TYPE)(void);

//在装饰器函数中内置的,用于为被装饰函数添加功能的回调函数
void innnerDectoratorCallbackableFunc(void *func)
{
	DECTORATORED_FUNC_TYPE  decFunc = (DECTORATORED_FUNC_TYPE)func;
	//为被装饰函数新增功能,这里仅仅用增加打印语句来表示新增功能
	printf("======为被装饰函数新增功能======\n");
	//调用被装饰的函数指针
	decFunc();
	printf("======被装饰函数执行完毕,退出装饰函数======\n");
}

//被装饰的函数
void dectoratoredFunc()
{
	printf("我是一个要被装饰的函数\n");
}

typedef struct
{
	INNER_CALLBACK_FUNC_TYPE    innerFunc;
	void						*funcArg;
}ReturnFuncType;

//装饰器函数
ReturnFuncType dectorator(void *func)
{
	//定义一个返回类型
	ReturnFuncType ret = {
		.innerFunc = innnerDectoratorCallbackableFunc,
		.funcArg   = (DECTORATORED_FUNC_TYPE)func
	};

	return ret;
}

int main(void)
{
	ReturnFuncType retFunc = dectorator((void *)dectoratoredFunc);
	retFunc.innerFunc(retFunc.funcArg);
	return 0;
}

输出:

bruce@bruce-PC:~/Code/exercise$ ./a.out 
======为被装饰函数新增功能======
我是一个要被装饰的函数
======被装饰函数执行完毕,退出装饰函数======
bruce@bruce-PC:~/Code/exercise$ 

语法

装饰器以特殊字符 @ 开头后跟装饰器的名字。
装饰器必须单独在一行(每个装饰器单独在一行)并且必须在一个被装饰函数定义的上一行
装饰器只能用来装饰函数,这个函数可以是一个模块中的函数,也可以是类函数;但是,装饰器就是不能用来装饰一个类
装饰器函数可以接收额外参数,也可以不接收额外参数,装饰器接收被装饰函数对象这个参数是必须的,不是可选的
在函数的封装器中,一般会重新定义一个新函数,在新函数中为被装饰函数新增某些功能,并调用被封装的函数,而这个重新被定义的函数对象会作为封装器的返回值返回。这一般称为闭包

注意: 装饰器所返回的函数对象的原型要和被装饰器函数的原型一致,否则无法通过被装饰器函数的函数名正确调用

具有单个装饰器的函数

def dec(func):
  print('******为函数安装装饰器******')
  def innerfunc():
    print('======为函数装饰新功能======')
    func()
  return innerfunc

@dec
def foo():
  print('======foo()======')

if __name__ == "__main__":
  print('*' * 10, '进入main函数中的第一条语句', '*' * 10)
  foo()

输出:

E:\Python3\Exercise\venv\Scripts\python.exe E:/Python3/Exercise/venv/02.py
******为函数安装装饰器******
********** 进入main函数中的第一条语句 **********
======为函数装饰新功能======
======foo()======
Process finished with exit code 0

上面装饰器的实质是:
@dec
def foo():

<等价于>
foo = dec(foo)

具有多个装饰器的函数

一个函数定义可以被一个或多个 decorator 表达式所包装。
当函数被定义时将在包含该函数定义的作用域中对装饰器表达式求值。 求值结果必须是一个可调用对象,它会以该函数对象作为唯一参数被发起调用。
其返回值将被绑定到函数名称上。
多个装饰器会以嵌套方式被应用。

def dec2(func):
  print('******在装饰器2中******')
  def innerfunc2():
    print('======为函数装饰新功能2======')
    func()
  return innerfunc2

def dec(func):
  print('******为函数安装装饰器******')
  def innerfunc():
    print('======为函数装饰新功能======')
    func()
  return innerfunc

@dec2
@dec
def foo():
  print('======foo()======')

if __name__ == "__main__":
  print('*' * 10, '进入main函数中的第一条语句', '*' * 10)
  foo()

输出:

E:\Python3\Exercise\venv\Scripts\python.exe E:/Python3/Exercise/venv/02.py
******为函数安装装饰器******
******在装饰器2******
********** 进入main函数中的第一条语句 **********
======为函数装饰新功能2======
======为函数装饰新功能======
======foo()======
Process finished with exit code 0

上面装饰器的实质是:
@dec2
@dec
def foo():

<等价于>
foo = dec2(dec(foo))

装饰器接收参数的函数

def dec(arg, arg2, arg3):
  print('arg = ', arg, 'arg2 = ', arg2, 'arg3 = ', arg3)
  def inner1(func):
    print('******为函数安装装饰器******')
    def inner2():
      print('======为函数装饰新功能======')
      func()
    return inner2
  return inner1

@dec(10, 100, 1000)
def foo():
  print('======foo()======')

if __name__ == "__main__":
  print('*' * 10, '进入main函数中的第一条语句', '*' * 10)
  foo()

输出:

E:\Python3\Exercise\venv\Scripts\python.exe E:/Python3/Exercise/venv/02.py
arg =  10 arg2 =  100 arg3 =  1000
******为函数安装装饰器******
********** 进入main函数中的第一条语句 **********
======为函数装饰新功能======
======foo()======
Process finished with exit code 0

上面装饰器的实质是:
@dec(10, 100, 1000)
def foo():

<等价于>
foo = dec(10, 100, 1000)(foo)

[上一页][下一页]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值