文档章节

利用世界杯,读懂 Python 装饰器

猫咪编程
 猫咪编程
发布于 07/20 14:59
字数 2283
阅读 2
收藏 0

Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性,

熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。

今天就结合最近的世界杯带大家理解下装饰器。

德国战车

6 月 17 日德国战墨西哥,小痴虽然是一个伪球迷,但每年的世界杯还是会了解下。而德国是上届的冠军,又是这届夺冠热门。德意志战车在 32 年间小组赛就没有输过!卧槽!虽然小痴很少赌球,但这次德国如此强大,肯定会赢吧。搏一搏单车变摩托!随后小痴买了德国队赢。心里想着这次肯定稳了!赢了会所嫩模!小痴连比赛都不看,美滋滋的敲着代码。

然后比赛结果却是德国爆冷 0:1 输给墨西哥队,德国队输了比赛,小痴也下海干活。只是此时的天台有点挤,风还有大。

小痴含泪的写下了下面的代码:

def guess_win(func):

def rooftop_status():

result = func()

print('天台已满,请排队!') return result return rooftop_status@guess_windef german_team():

print('德国必胜!')

复制代码

输出结果:

德国必胜!

天台已满,请排队!

复制代码

装饰器是什么

首先我们先来了解下什么是装饰器,严格来说,装饰器只是语法糖,装饰器是可调用的对象,可以像常规的可调用对象那样调用,特殊的地方是装饰器的参数是一个函数。

装饰器的存在是为了适用两个场景,一个是增强被装饰函数的行为,另一个是代码重用。

比如在上面的例子中我们在压德国队赢的时候,原本的 german_team() 函数只是输出德国必胜,但在使用装饰器(guess_win)后,它的功能多了一项:输出「天台已满,请排队!」。这就是一个简单的装饰器,实现了「增强被装饰函数的行为」。

一个良好的装饰器必须要遵守两个原则:

1 不能修改被装饰函数的代码

2 不能修改被装饰函数的调用方式

这里并不难以理解,在现在的生产环境中,很多代码是不能轻易的改写,因为这样有可能发送意想不到的影响。还有一点就是我们在看大神的代码,我们根本不懂如何改写。同时你也不能修改调用方式,因为你并不知道有在一个项目中,有多少处应用了此函数。

装饰器理解基础

如果你想要很好的理解装饰器,那下面的两个内容需要你先有所认知。

1 函数名可以赋值给变量

2 高阶函数

1 函数名可以赋值给变量

我们来看下这个例子:

def func(name):

print('我是{}!慌的一逼!'.format(name))

func('梅西')

y = func

y('勒夫')

复制代码

输出结果:

我是梅西!慌的一逼!

我是勒夫!慌的一逼!

复制代码

在代码中我们首先定义了函数 func,并调用了 func 函数,并且把 func 赋值给 y。y = func 表明了:函数名可以赋值给变量,并且不影响调用。

这样讲,可能还有些人不太明白。我们在来对比下我们常用的操作。这其实和整数、数字是一样的,下面的代码你肯定熟悉:

a = 1

b = a

print(a, b)

复制代码

2 高阶函数

高阶函数满足如下的两个条件中的任意一个:a.可以接收函数名作为实参;b.返回值中可以包含函数名。

在 Python 标准库中的 map 和 filter 等函数就是高阶函数。

l = [1, 2, 4]

r = map(lambda x: x*3, l)for i in r:

print('当前天台人数:', i)

复制代码

输出结果:

当前天台人数: 3

当前天台人数: 6

当前天台人数: 12

复制代码

自定义一个能返回函数的函数,也是高阶函数:

def f(l):

return map(lambda x: x *5, l)

a = f(l)for i in a:

print('当前天台人数:', i)

复制代码

输出结果:

当前天台人数: 5

当前天台人数: 10

当前天台人数: 20

复制代码

实现一个类似的装饰器

现在你已经知道了「函数名赋值」和「高阶函数」,有了这两个基础,我们就可以尝试实现一个类似的装饰器。

def status(func):

print('慌的一逼!') return funcdef name():

print('我是梅西!')

temp = status(name)

temp()

复制代码

输出结果:

慌的一逼!

我是梅西!

复制代码

在这个例子中我们定义了一个 status 函数,status 接收一个函数名然后直接返回该函数名。这样我们实现了不修改原函数 name,并且添加了一个新功能的需求。但是这里有个缺陷就是函数的调用方式改变了。即不是原本的 name,而是 temp。

要解决这个问题很简单,相信 a = a*3 这样的表达式大家都见过,那么上述代码中的 temp = status(name) 同样可以修改为 name = status(name),这样我们就完美的解决了问题:既添加新功能又没有修改原函数和其调用方式。修改后的代码如下:

def status(func):

print('慌的一逼!') return funcdef name():

print('我是梅西!')

name = status(name)

name()

复制代码

但这样的代码却有个不便之处,即每次使用这样的装饰器,我们都要写类似 name = status(name) 的代码。程序员都是懒的,所以才有那么多高级的语法。在 python 中为了简化这种情况,提供了一个语法糖 @,在每个被装饰的函数上方使用这个语法糖就可以省掉这一句代码 name = status(name),最后的代码如下:

def status(func):

print('慌的一逼!') return func@statusdef name():

print('我是梅西!')

name()

复制代码

这样我们就弄清楚了装饰器的工作原理:

1 写一个高阶函数,即参数是函数,返回的也是函数。

2 在利用语法糖@,简化赋值操作。

但是对比开头的例子,还是有些不一样。在开始的例子中,我们还实现了一个 rooftop_status 函数,来判断下当前的天台状是否人满。但是我们现在是直接返回了函数名,这样函数调用后我们就没办法做任何事情。梅西和德国慌了,我们也慌了,各个都要天台见,但在这之前我们也要考虑下天台的情况。

为了能判断天台的情况,所以此时我们需要在嵌套一层函数,将实现额外功能的部分写在内层函数中,然后将这个内层函数返回即可。这也是为什么装饰器都是嵌套函数的原因。

另外,开篇的例子并没有返回值,也没有参数,要对既有参数又有返回值的函数进行装饰的话,还需要进一步完善。 能够处理返回值的装饰器:

def guess_win(func):

def rooftop_status():

result = func()

print('天台已满,请排队!') return result return rooftop_status@guess_windef german_team():

print('德国必胜!') return '赢了会所嫩模!输了下海干活!'x = german_team()

print(x)

复制代码

输出结果:

德国必胜!

天台已满,请排队!

赢了会所嫩模!输了下海干活!

复制代码

能够处理参数的装饰器:

def guess_win(func):

def rooftop_status(*args, **kwargs):

result = func(*args, **kwargs)

print('天台已满,请排队!') return result return rooftop_status@guess_windef german_team(arg):

print('{}必胜!'.format(arg)) return '赢了会所嫩模!输了下海干活!'x = german_team('德国')

y = german_team('西班牙')

print(x)

复制代码

输出结果:

德国必胜!

天台已满,请排队!

西班牙必胜!

天台已满,请排队!

赢了会所嫩模!输了下海干活!

复制代码

总结

装饰器的本质是函数,其参数是另一个函数(被装饰的函数)。装饰器通常会额外处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。行为良好的装饰器可以重用,以减少代码量。

对于这届的世界杯,我总结了下。

我有一个微信公众号,经常会分享一些python技术相关的干货;如果你喜欢我的分享,可以用微信搜索“python语言学习”关注

欢迎大家加入千人交流答疑裙:699+749+852

© 著作权归作者所有

共有 人打赏支持
猫咪编程
粉丝 5
博文 28
码字总数 41050
作品 0
石家庄
利用世界杯,读懂 Python 装饰器

Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性,熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。 今天就结合最近的世界杯带大家理解下装饰器。...

p柯西
07/18
0
0
转载:唐磊的个人博客《python中decorator详解》【转注:深入浅出清晰明了】

转载请注明来源:唐磊的个人博客《python中decorator详解》 前面写python的AOP解决方案时提到了decorator,这篇文章就详细的来整理下python的装饰器——decorator。 python中的函数即objects...

laugh2last
2015/08/17
0
0
python利用装饰器进行运算

今天想用python的装饰器做一个运算,代码如下 >>> def mu(x):def _mu(args,**kwargs):return xxreturn _mu @mudef test(x,y):print '%s,%s' %(x,y) test(3,5) Traceback (most recent call l......

jastme
2015/07/24
0
0
一文读懂 with ... as 语句的来龙去脉

说到 with 大家通常看到的应该是这样的: 示例 1 打开一个文件,然后循环做一些事情。但是你知道为什么会有 with 吗?我们自己是不是能够写出可以作用在 with 关键字上的对象呢? 现在,我们...

技术小能手
08/08
0
0
设计模式之:理解 Python 中的装饰器

1、问题 文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: @makebold @makeitalic def say(): return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出......

大数据之路
2013/07/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

springboot 上传图片(多张和单张都可以)

@RequestMapping("/upload")@ResponseBodypublic String handleFileUpload(HttpServletRequest request) { MultipartHttpServletRequest params = ((MultipartHttpServletRequest) req......

MrBoyce
6分钟前
1
0
Android Studio 3.X打开DDMS

Android Studio更新到3.0版本以后Tools菜单变成了这样了 DDMS入口找不到了 在设置里找到SDK的路径 打开tools文件夹 双击打开 monitor.bat文件 会出来一个黑色命令行框,稍等一下,熟悉的DDM...

lanyu96
11分钟前
1
0
js 进一法、四舍五入法、舍去法 保留两位小数

function format_number(num,len) { len = len > 0 && len <= 20 ? len : 2; var result = parseFloat(num); num=isNaN(result)?0:result; var numpow=Math.pow(10,len)......

xiaogg
17分钟前
0
0
Node.js中的不安全跳转如何防御详解

Node.js中的不安全跳转如何防御详解 导语: 早年在浏览器大战期间,有远见的Chrome认为要运行现代Web应用,浏览器必须有一个性能非常强劲的Java引擎,于是Google自己开发了一个高性能的开源的...

开元中国2015
18分钟前
1
0
国内首个区块链试验区在海南成立

据新华社报道,10月8日,海南自贸区(港)区块链试验区正式在海南生态软件园授牌设立,这也是目前为止国内第一个区块链试验区。 该试验区位于海南生态软件园,与试验区同一天成立还有2家研究...

问题终结者
20分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部