python 函数装饰_Python 函数装饰器

首次接触到装饰器的概念,太菜啦!

Python 装饰器可以大大节省代码的编写量,提升代码的重复使用率。函数装饰器其本质也是一个函数,我们可以把它理解为函数中定义了一个子函数。

例如我们有这么一个需求,每次执行一个函数后,需要知道这个函数执行了多长时间。一般情况下,我会这样写:

defAccumulate(n):"""accumulate number which is from 1 to n"""s=0;for i in range(1,n+1):

s= s +i;returns;importtime;

start=time.time();

result= Accumulate(1000000);

end=time.time();print("funtion run time %.5f" % (end - start));

问题来了,如果又来了一个函数呢?重复上面的代码吗?天啦噜,来一百个函数岂不玩完了。这个时候就要用到装饰器了,将于具体函数功能不相关的内容全部写进装饰器中,这样一来,附加功能与函数功能就能完全分离。提升代码的使用率。

defFunctionRunTime(func):def warper(*args,**kwargs):

start=time.time();

result= func(*args,**kwargs);

end=time.time();print("function run time %.5f" % (end -start));returnresult;returnwarper;

@FunctionRunTimedefAccumulate(n):"""accumulate number which is from 1 to n"""s=0;for i in range(1,n+1):

s= s +i;return s;

result = Accmulate(1000000);

如此一来,打印函数运行时间的部分就实现了重用。如果在来一个函数,只需在函数声明时加上@FunctionRunTime 即可。如果不想这么写还可以有另外一种写法,因为装饰器本质上是一个函数,可以使用函数传参的方式将装饰器加到目标函数上。

Acc =FunctionRunTime(Accumulate);

result= Acc(100000);

这样的话就降低了代码的可读性,最好还是用@符,清爽,不产生语义偏差。

如果函数是一个递归函数呢?因为装饰器是将函数包裹起来的外层函数,也就是说装饰器和函数执行次数一样多。如果函数是递归函数,那么装饰器是不是也同样执行多次呢?带着这个问题,做一下实验。

@FunctionRunTimedeffib(n):if n ==0:return0;elif n == 1:return 1;else:return fib(n-1) + fib(n-2);

da96541c750cbc8fe55a02fc0d90595e.png

实验结果说明,装饰器也同样执行多次。那么这个问题如何解决呢?还是回归到装饰器的特点上来说。装饰器本质是一个函数,而且是将目标函数包裹起来,它只与目标函数有关。现在的问题是目标函数是递归执行多次,所起装饰器也执行多次。如果将目标函数从递归函数变成非递归函数不就将问题解决了吗?如何实现这个转换又成为了另一个问题。解决方法很巧妙,我们只需要在目标函数外在套一层函数,让具有递归特性的函数成为一个子函数,那么它就不再是装饰器包裹的对象了。

@FunctionRunTimedeffib(n):def_fib(n):if n ==0:return0;elif n == 1:return 1;else:return _fib(n-1) + _fib(n-2);return _fib(n);

c943890c4f02f3a5b55e3ca099dcdc0a.png

从函数上来看,此时的fib 函数就是一个普通的函数,不具有递归特性。

装饰器的一些小问题

装饰器既然也是函数,那么就可以传参。通过传递参数可以使装饰器实现更加复杂的功能。

defWithParamsDecorator(printName):defFunctionRunTime(func):def warper(*args,**kwargs):

start=time.time();

result= func(*args,**kwargs);

end=time.time();print("function run time %.5f" % (end -start));if(printName):print("this is %s function" % func.__name__);returnresult;returnwarper;returnFunctionRunTime;

@WithParamsDecorator(printName=True)defAccumulate(n):"""accumulate number which is from 1 to n"""s=0;for i in range(1,n+1):

s= s +i;return s;

d97bbcef7ae1bb3dd1bcb38744b41d87.png

被装饰器装饰后的函数已经不再是它自己。使用@符很好的规避了语义上的差异,使程序的可读性保持不变,但要深入理解装饰器还得看其执行过程。回到不带参数的装饰器那个例子。Accumulate 实际上是Accumulate = FunctionRunTime(Accumulate); 此时的Accumulate 其实是warper 函数。下面看个简单的验证。

ca92ee69420e58bb8c22029729ccd2e4.png

所以,真实的情况是Accumulate 是warper 的函数句柄,只不过名称叫Accumulate 让我们在视觉上觉得它还是原来的Accumulate ,极具迷惑性。

在大牛的肩膀上跳跃。

参考文献

https://www.zhihu.com/question/26930016

http://wepon.me/2015/08/03/Python-decorator-recursion/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值