Python 函数装饰器
首次接触到装饰器的概念,太菜啦!
Python 装饰器可以大大节省代码的编写量,提升代码的重复使用率。函数装饰器其本质也是一个函数,我们可以把它理解为函数中定义了一个子函数。
例如我们有这么一个需求,每次执行一个函数后,需要知道这个函数执行了多长时间。一般情况下,我会这样写:
def Accumulate(n):
"""accumulate number which is from 1 to n"""
s = 0;
for i in range(1,n+1):
s = s + i;
return s; import time;
start = time.time();
result = Accumulate(1000000);
end = time.time();
print("funtion run time %.5f" % (end - start));
问题来了,如果又来了一个函数呢?重复上面的代码吗?天啦噜,来一百个函数岂不玩完了。这个时候就要用到装饰器了,将于具体函数功能不相关的内容全部写进装饰器中,这样一来,附加功能与函数功能就能完全分离。提升代码的使用率。
def FunctionRunTime(func):
def warper(*args,**kwargs):
start = time.time();
result = func(*args,**kwargs);
end = time.time();
print("function run time %.5f" % (end - start));
return result;
return warper; @FunctionRunTime
def Accumulate(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);
这样的话就降低了代码的可读性,最好还是用@符,清爽,不产生语义偏差。
如果函数是一个递归函数呢?因为装饰器是将函数包裹起来的外层函数,也就是说装饰器和函数执行次数一样多。如果函数是递归函数,那么装饰器是不是也同样执行多次呢?带着这个问题,做一下实验。
@FunctionRunTime
def fib(n):
if n == 0:
return 0;
elif n == 1:
return 1;
else:
return fib(n-1) + fib(n-2);

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

从函数上来看,此时的fib 函数就是一个普通的函数,不具有递归特性。
装饰器的一些小问题
装饰器既然也是函数,那么就可以传参。通过传递参数可以使装饰器实现更加复杂的功能。
def WithParamsDecorator(printName):
def FunctionRunTime(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__);
return result;
return warper;
return FunctionRunTime; @WithParamsDecorator(printName=True)
def Accumulate(n):
"""accumulate number which is from 1 to n"""
s = 0;
for i in range(1,n+1):
s = s + i;
return s;

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

所以,真实的情况是Accumulate 是warper 的函数句柄,只不过名称叫Accumulate 让我们在视觉上觉得它还是原来的Accumulate ,极具迷惑性。
在大牛的肩膀上跳跃。
参考文献
https://www.zhihu.com/question/26930016
http://wepon.me/2015/08/03/Python-decorator-recursion/
Python 函数装饰器的更多相关文章
- Python函数装饰器原理与用法详解《摘》
本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...
- python函数-装饰器
python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...
- Python函数装饰器高级用法
在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...
- Python @函数装饰器及用法
1.函数装饰器的工作原理 函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn ...
- Python @函数装饰器及用法(超级详细)
函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn() # 执行传入的fn参 ...
- Python高手之路【四】python函数装饰器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
- python 函数 装饰器 内置函数
函数 装饰器 内置函数 一.命名空间和作用域 二.装饰器 1.无参数 2.函数有参数 3.函数动态参数 4.装饰器参数 三.内置函数 salaries={ 'egon':3000, 'alex':10 ...
- Python 函数装饰器简明教程
定义类的静态方法时,就使用了装饰器.其实面向对象中的静态方法都是使用了装饰器. @staticmethod def jump(): print(" 3 meters high") ...
- Python高手之路【四】python函数装饰器,迭代器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
随机推荐
- Linux分区设置
基本3个就可以了 序号 路径 大小 格式 ① /boot 200MB ext4 ② 物理内存*1-1.5 swap ③ / 剩余存储空间 ext4
- machine_desc结构体【转】
转自:http://blog.csdn.net/myarrow/article/details/8609564 1. 简介 内核提供了一个重要的结构体struct machine_desc ,这个结构 ...
- Linux内核驱动之GPIO子系统(一)GPIO的使用【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/9427047 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt ...
- 转载:(Mac)在bash和zsh配置环境变量path的几种方法
参考文献 老习惯,列出本文参考或引用或转载的文档和博客,致以崇高的敬意,感兴趣的可以去看看 1.http://postgresapp.com/ 2.http://postgresapp.com/doc ...
- Lucas卢卡斯定理
当$p$为素数时 $$C_n^m\equiv C_{n/p}^{m/p}*C_{n\%p}^{m\%p}(mod\ p)$$ 设$n=s*p+q,m\equiv t*p+r(q,r<=p)$ 我 ...
- unbuntu中如何像Windows一样顺畅的切换中英文输入法
1.首先在unbuntu安装搜狗拼音输入法(这个不用教了) 2.点击右上角的搜狗拼音的图标点击设置进入设置页面 3.选择高级 4.选择Fcitx设置 5.添加输入法英语(美国) 6.在设置中选择按键, ...
- Android 图片平铺效果
我们大家都看过平铺的效果,那么我们都是怎么样才能实现的那,我们其实主要用到的就是api,我们一开始new一个bitmap,就可以了,但是,大家都没有想过,我们还可以用什么方法来做这个事情那,那么我们就 ...
- 重装windows系统后配置Anaconda
给电脑换了系统,十分担心anaconda需要重装.还好以下方法完美解决.(同是win10 64位) 原始anaconda安装路径:D:\ProgramData\Anaconda3 (不能有空格哦) ...
- 多CPU,多核,多进程,多线程
当面临这些问题的时候,有两个关键词无法绕开,那就是并行和并发. 首先,要先了解几个概念: 1.进程是程序的一次执行. 2.进程是资源分配的基本单位(调度单位). 3.一个进程可以包括多个线程. 4.在 ...
- lnmp创建站点
一.创建站点 1.输入命令 lnmp vhost add 输入域名 www.xxx.com 回车 回车 回车 y创建 n不创建 网站如果有目录权限 更改目录权限 chown -R www:www /h ...