(转)python装饰器进阶一
Python装饰器进阶之一
先看例子
网上有很多装饰器的文章,上来说半天也没让人看明白装饰器到底是个什么,究竟有什么用,我们直接来看几个例子。
Python递归求斐波那契数列
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50))
这是一个很常见的求斐波那契数列的例子,但是这其中有非常多的重复计算。
如:我们要计算第10位数字是多少,就要先计算出第9位数字是多少,并且是从0计算到9。此时我们已经计算过0-9位数字的值,甚至已经知道第10位数字的值,但是当计算第11位的时候却又要重复一遍。也就是说我们如果计算第50位数字是多少,就要先计算0-1,0-2,0-3 ... 0-48,0-49,0-50。数字越大,重复的计算就越多。上面的程序有兴趣的可以运行一下,我等待了五分钟没有出结果就强行在任务管理器关闭了。
略微修改一下
# 增加一个缓存字典
def fibonacci(n, cache=None):
# 第一次没有任何缓存,初始化字典
if cache is None:
cache = {}
# 如果n这个数字,是我们已经计算过的,就不需要再去递归从头计算了
if n in cache:
return cache[n]
if n <= 1:
return 1
# 如果缓存没有,就把它增加到缓存中去
cache[n] = fibonacci(n - 1, cache) + fibonacci(n - 2, cache)
return cache[n]
print(fibonacci(50))
运行结果:
20365011074
[Finished in 0.1s]
前面运行五分钟没有任何结果,而简单修改以后仅仅用了0.1秒,可见重复计算对性能的影响用几何倍数称呼都不为过。
思考:
这种重复计算的问题,在我们的项目中是非常常见的,特别是一些计算概率的方法,都需要重复计算。那么我们总不能每个方法都这样改写一次吧?那样我们的项目中会包含大量的冗余代码,不利于我们的维护,也违背了我们高内聚,低耦合的编程思想。那我们能否实现,只用写一个增加缓存的方法,然后其他的方法我们都使用这个方法来装饰它,让它拥有这个功能。
实现思路:
- 写一个方法假设方法名为add_cache,该方法拥有缓存
- add_cache接收一个参数,参数就是原方法的对象
- 在add_cache方法内部,有一个wrap方法,该方法就接收原来fibonacci方法的参数
- wrap方法首先判断参数是否在缓存中,如果在,就直接返回缓存中的值
- 如果不在缓存中就调用原来的fibonacci方法计算然后把值保存在缓存中
- 在add_cache中返回新的wrap方法的对象
举个栗子
# 装饰方法
def add_cache(func):
cache = {}
# 因为方法可能不是都只有一个参数,所以我们使用这种形式可以接收任意个参数
# wrap可以随便起名的,还有add_cache都是随便起名字不是固定的
def wrap(*args):
# 如果这些参数不在cache中
if args not in cache:
# 调用原方法,并且把这些参数对应的值存储在cache中
# 需要注意的是,传递给原方法我们要把元组形式的args拆分成单个参数
cache[args] = func(*args)
# 在cache中就直接返回
return cache[args]
# 我们需要把新的方法对象返回回去,这样用户调用add_cache传递旧的方法就能得到新的方法对象
return wrap
# 计算斐波那契的方法
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
"""
调用装饰方法,获得新的增加了功能的对象,通常新方法名保持和原方法名一致,这样原来的业务逻辑不需要做任何修改
"""
fibonacci = add_cache(fibonacci)
# 需要注意的是,Python的数值也是有长度限制的,在Win10x64计算机上,最高可以计算第332位
print(fibonacci(50))
看完上面的例子以后,有人可能会说:这不是忽悠人呢?你以为我是新手啊,不知道什么是装饰器吗?装饰器我看人家都是用@符号后面跟个名字,然后写在方法上就可以了。
我想说,之所以能够实现@符号装饰,是因为Python给我们提供了这样的便利,再来看上面的例子:
def add_cache(func):
cache = {}
def wrap(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrap
# 装饰fibonacci方法,只用一个@符号,后跟自己写的装饰方法即可
@add_cache
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
# 此时这个fibonacci实际上已经被增加了新的功能
print(fibonacci(50))
同样能够实现之前的功能。
提醒:我之所以不厌其烦的复制重复代码,并且一行一行删除掉已经写过的注释,就是希望能加深大家的印象,能把别处学来的东西,真正理解,应用到工作中,变成自己的东西。
至此,我相信大家已经明白什么是装饰器了。所谓装饰器,就是给我们原来非常单调,没有某些功能的方法,增加一些功能,就跟我们玩游戏给人物带装备一样。这么想是不是非常容易理解了。
代码我在Python2.x和3.x都测试过没问题
废话不想说那么多,希望大家好好学习,工资蹭蹭往上涨。
by. 秋名山车神
END
作者: 秋名山车神
链接:http://www.imooc.com/article/16227
来源:慕课网
(转)python装饰器进阶一的更多相关文章
- Python装饰器进阶
装饰器进阶 现在,我们已经明白了装饰器的原理.接下来,我们还有很多事情需要搞清楚.比如:装饰带参数的函数.多个装饰器同时装饰一个函数.带参数的装饰器和类装饰器. 装饰带参数函数 def foo(fun ...
- (转)python装饰器二
Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...
- Python函数--装饰器进阶
开放封闭原则 1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代码扩展.添加新功能. 2.对修改是封 ...
- day 12 - 1 装饰器进阶
装饰器进阶 装饰器的简单回顾 装饰器开发原则:开放封闭原则装饰器的作用:在不改变原函数的调用方式的情况下,在函数的前后添加功能装饰器的本质:闭包函数 装饰器的模式 def wrapper(func): ...
- Python—装饰器详解
装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...
- day4之装饰器进阶、生成器迭代器
装饰器进阶 带参数的装饰器 # 某一种情况# 500个函数加装饰器, 加完后不想再加这个装饰器, 再过一个季度,又想加上去# 你可以设计你的装饰器,来确认是否执行 # 第一种情况 # 想要500个函数 ...
- 五分钟学会Python装饰器,看完面试不再慌
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第12篇文章,我们来看看Python装饰器. 一段囧事 差不多五年前面试的时候,我就领教过它的重要性.那时候我Pyt ...
- Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案
本文为霍格沃兹测试学院学员学习笔记. Python 装饰器简介 装饰器(Decorator)是 Python 非常实用的一个语法糖功能.装饰器本质是一种返回值也是函数的函数,可以称之为“函数的函数”. ...
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
随机推荐
- github readme.md 添加图片
简要: 将图片放在仓库里面,在文件里链接它,最后 push 到 github 上. github 图片链接格式: (http://github.com/yourname/your-repository ...
- OV7725学习之SCCB协议(一)
OV7725摄像头只能作为从机,通过SCCB协议配置内置的172个寄存器.因此首先要了解的就是SCCB总线 1.SCCB协议简述 SCCB协议有两线也有三线,两线为SIO_C与SIO_D,三线为SIO ...
- 【LeetCode】Excel Sheet Column Number(Excel表列序号)
这道题是LeetCode里的第171道题. 题目描述: 给定一个Excel表格中的列名称,返回其相应的列序号. 例如, A -> 1 B -> 2 C -> 3 ... Z -> ...
- Linux下如何挂载和卸载硬盘?
fdisk -l 查看所有被系统识别的磁盘 df -h 查看磁盘占用情况 sudo umount -v /media 卸载挂载点的硬件 df -T 查看所有磁盘的文件系统类型(type) mount ...
- mybitis中对象字段与表中字段名称不匹配(复制)
开发中,实体类中的属性名和对应的表中的字段名不一定都是完全相同的,这样可能会导致用实体类接收返回的结果时导致查询到的结果无法映射到实体类的属性中,那么该如何解决这种字段名和实体类属性名不相同的冲突呢? ...
- win 8系统下如何安装搭建python
python的环境搭建除了python本身,还有Aptana和pip的安装.Aptana提供了更好的集成开发环境,pip主要用于安装第三方的包. 具体安装流程可参考以下两篇文章: InSky关于安装p ...
- hihoCoder #1783 又一个重复计数
题目大意 给定一个长度为 $n$ 的字符串 $S$,定义函数 $f(S)$ 表示 $S$ 的不同回文子串的个数.对于 $1\le l \le r \le n$,定义 $S[l,r]$ 为字符串 $S$ ...
- WF 18 A 想法
UPD:我理解错题意了. 考虑在时刻 $t$ 从站点 $u$ 出发的公交车,将这些车的集合记做 $B(u,t)$,$B(u,t)$ 是个随机变量. 令 $\mathrm{Pr}_{B(u,t)} = ...
- 挑战程序竞赛例题 4.1 Random Walk(高斯消元求期望值)
给你一幅N*M的地图,地图中有不能到达的障碍物'#'与可以走的点'.',从(1,1)开始走到(N,M),其中每一次走动均等概率地向周围的可达的格子走去,求到达(N,M)的期望步数.(N,M<=1 ...
- One-Way Streets (oneway)
One-Way Streets (oneway) 题目描述 Once upon a time there was a country with nn cities and mm bidirection ...