摘要:今天就结合最近的世界杯带大家理解下装饰器。而德国是上届的冠军,又是这届夺冠热门。装饰器的存在是为了适用两个场景,一个是增强被装饰函数的行为,另一个是代码重用。在利用语法糖,简化赋值操作。行为良好的装饰器可以重用,以减少代码量。
Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性,
熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。
今天就结合最近的世界杯带大家理解下装饰器。
德国战车6 月 17 日德国战墨西哥,小痴虽然是一个伪球迷,但每年的世界杯还是会了解下。而德国是上届的冠军,又是这届夺冠热门。德意志战车在 32 年间小组赛就没有输过!卧槽!虽然小痴很少赌球,但这次德国如此强大,肯定会赢吧。搏一搏单车变摩托!随后小痴买了德国队赢。心里想着这次肯定稳了!赢了会所嫩模!小痴连比赛都不看,美滋滋的敲着代码。
然后比赛结果却是德国爆冷 0:1 输给墨西哥队,德国队输了比赛,小痴也下海干活。只是此时的天台有点挤,风还有大。
小痴含泪的写下了下面的代码:
def guess_win(func): def rooftop_status(): result = func() print("天台已满,请排队!") return result return rooftop_status @guess_win def 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 func def 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 func def name(): print("我是梅西!") name = status(name) name()
但这样的代码却有个不便之处,即每次使用这样的装饰器,我们都要写类似 name = status(name) 的代码。程序员都是懒的,所以才有那么多高级的语法。在 python 中为了简化这种情况,提供了一个语法糖 @,在每个被装饰的函数上方使用这个语法糖就可以省掉这一句代码 name = status(name),最后的代码如下:
def status(func): print("慌的一逼!") return func @status def name(): print("我是梅西!") name()
这样我们就弄清楚了装饰器的工作原理:
1 写一个高阶函数,即参数是函数,返回的也是函数。
2 在利用语法糖@,简化赋值操作。
但是对比开头的例子,还是有些不一样。在开始的例子中,我们还实现了一个 rooftop_status 函数,来判断下当前的天台状是否人满。但是我们现在是直接返回了函数名,这样函数调用后我们就没办法做任何事情。梅西和德国慌了,我们也慌了,各个都要天台见,但在这之前我们也要考虑下天台的情况。
为了能判断天台的情况,所以此时我们需要在嵌套一层函数,将实现额外功能的部分写在内层函数中,然后将这个内层函数返回即可。这也是为什么装饰器都是嵌套函数的原因。
另外,开篇的例子并没有返回值,也没有参数,要对既有参数又有返回值的函数进行装饰的话,还需要进一步完善。 能够处理返回值的装饰器:
def guess_win(func): def rooftop_status(): result = func() print("天台已满,请排队!") return result return rooftop_status @guess_win def 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_win def german_team(arg): print("{}必胜!".format(arg)) return "赢了会所嫩模!输了下海干活!" x = german_team("德国") y = german_team("西班牙") print(x)
输出结果:
德国必胜! 天台已满,请排队! 西班牙必胜! 天台已满,请排队! 赢了会所嫩模!输了下海干活!总结
装饰器的本质是函数,其参数是另一个函数(被装饰的函数)。装饰器通常会额外处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。行为良好的装饰器可以重用,以减少代码量。
对于这届的世界杯,我总结了下。
本文首发与公众号「痴海」,后台回复「1024」,领取 2018 最新 python 教程。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42000.html
摘要:时代,如果需要手动继承,如多态多态是指,不同的子类对象调用相同的父类方法,会产生多态多样结果的编程特性。 参考:黑马程序员教程 - Python基础 面向对象 OOP三大特性,且三个特性是有顺序的: 封装 继承 多态 封装 指的就是把现实世界的事务,封装、抽象成编程里的对象,包括各种属性和方法。这个一般都很简单,不需要多讲。 唯一要注意的就是:推荐从小往大开始封装、开发类。比如手枪...
摘要:列表越界的列表类似于动态数组,没有长度的限制。比如对将要传进内层函数的参数进行检测等,从而实现对参数的类型进行限制。对二维列表的每一维列表进行长度限制,不足指定长度,自动补充指定元素。 前言 作为一名python的脑残粉,请先跟我念一遍python大法好。 其作为动态语言的灵活,简介的代码,确实在某些情况下确实比其他编程语言要好。但你有没有想过,有时这些灵活的语法,可能会造成一些糟糕的...
摘要:我们以测量函数运行时间为例来讲一讲装饰器的运行原理。三更加通用的装饰器前面两部分讲了装饰器的原理,这一部分就讲讲要写出一个通用的装饰器需要注意的问题。首先就是参数的问题,装饰器返回的函数不是原来的函数,函数的签名也就和原来的函数签名不一样。 一、最简单的装饰器 装饰器是python中很基础也很实用的一个特性。通过装饰器我们可以很方便地为一些函数添加相同的功能。我们以测量函数运行时间为例...
摘要:函数装饰器和闭包严格来说,装饰器只是语法糖。何时执行装饰器它们在被装饰的函数定义之后立即运行。装饰器突出了被装饰的函数的作用,还便于临时禁用某个促销策略只需把装饰器注释掉。 函数装饰器和闭包 严格来说,装饰器只是语法糖。如前所示,装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。 Python何时执行装饰器 它们在...
阅读 2608·2021-10-14 09:47
阅读 4877·2021-09-22 15:52
阅读 3331·2019-08-30 15:53
阅读 1403·2019-08-30 15:44
阅读 646·2019-08-29 16:41
阅读 1619·2019-08-29 16:28
阅读 419·2019-08-29 15:23
阅读 1590·2019-08-26 12:20