P百科:如何理解 Python 中的装饰器

昨天

【P百科】:Python 大白话百科全书
每周,痴海会用大白话,讲解一个 Python 中那些难以理解的知识点。
 
希望通过这样的白话讲解,可以让大家更好的学会 Python。
今天这篇文章的主题是:大白话讲解 Python 装饰器;
Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性,熟练掌握装饰器会让你得到以下几个优势:
  1. 程序可读性高
  2. 代码复用性高
  3. 你的编程思路更加宽广
  4. 程序更加 pythonic,更像专业写 Python 代码
但对于新手来说 Python 装饰器非常难以理解:
  1. 不清楚什么是装饰器
  2. 不清楚装饰器有什么用
  3. 不清楚如何写装饰器代码
所以痴海今天的 P 百科,会带大家彻底的弄明白 Python 装饰器。
文章主要从下面 4 个方面进行讲解:
1 装饰器是什么
2 装饰器理解基础
3 实现一个装饰器
4 装饰器总结
1 装饰器是什么
俗话说:艺术来源于生活。
痴海说:编程也来自生活。
编程的世界也是如此,很多编程世界里的概念,都是源自生活中的方方面面。
每个编程语言被创造出来,就是为了解决现实世界某个具体的问题。
而我们的今天的主角「装饰器」也是如此。
1.1 那什么是装饰器?
我们来看一个生活中具体的案例:手机钢化膜。
现在基本人手一台手机,大家也知道手机屏幕摔倒地板,是非常容易坏。
手机屏幕一坏,大家都非常心痛,因为如果拿出维修,费用都够重新购买一台新的手机了。
那我们要怎样在不破坏原本手机屏幕结构的同时,让我们的手机更耐坏呢?
这时候你只需要花几块钱购买一个钢化膜,来装饰我们的手机。
有了这块钢化膜,从此你就不怕手机摔倒地上。
手机钢化膜就是现实中的装饰器:钢化膜在不改变原有手机的结构下,让手机屏幕变的更耐摔。
而我们编程中的装饰器,同样的道理:装饰器可以在不改变原有函数的结构下,让程序更加强大。
装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。
1.2 装饰器有什么作用
通过上面的例子你现在应该是能明白什么是装饰器:用来装饰函数用的,让函数更加强大。
但你现在还是不知道 Python 里的装饰器长什么样,先不着急,我们先来看下装饰器有什么作用。
那装饰器都有什么作用?
简单说:增强函数的功能。
如果你想在不改变原有函数的基础上,增加新的函数功能,那装饰器就非常适合你。
装饰器的存在是为了适用两个场景:
1 一个是增强被装饰函数的行为;
2 另一个是代码复用;
具体举几个例子:
  • 插入日志
  • 性能测试
  • 事务处理
  • 缓存
  • 权限校验
上面都是实际工作中会经常遇到的场景。
1.3 小结
看到这里你应该是可以明白以下几个知识点:
a 什么是装饰器
装饰器可以在不改变原有函数的结构下,让程序更加强大。
b 装饰器有什么作用
增强函数的功能
c 装饰器适用的两个场景
a1 增强被装饰函数的行为;
b1 代码复用;
有了以上 3 个学前知识,你现在应该对装饰器,有了基本的认知。
但你仍然不知道装饰器的代码如何编写,所以接下来我们就手把手教会大家如何编写一个 Python 装饰器代码。
同样在编写装饰器代码之前,有 2 个基础你一定要先知道。
a Python 中的函数也是变量
b Python 中的高阶函数
我们来具体讲解这个 2 个基础。
2 装饰器理解的基础
2.1 Python 中的函数也是变量
Python 中的函数可以像普通变量一样,当做参数传递给另外一个函数。
我们来看下这个案例1:输出“痴海666”
1def chihai():2    print('痴海666')34chihai()5y = chihai6y()
输出结果:
1痴海6662痴海666
在代码中我们首先定义了函数 chihai,并调用了 chihai 函数,并且把 chihai 赋值给 y。
y = chihai 表明了:函数名可以赋值给变量,并且不影响调用。
这样讲,可能还有些人不太明白。
我们在来对比下我们常用的操作。
这其实和整数、数字是一样的,
下面的代码你肯定熟悉:
1a = 12b = a3print(a, b)

2.2 Python 中的高阶函

什么是高阶函数?
非常简单,只要满足如下的两个条件中的任意一个:
a. 可以接收函数名作为实参;
b. 返回值中可以包含函数名。
一个函数接收另一个函数作为参数,这种函数称之为高阶函数。
这样的一个函数就是高阶函数,同样我们改造下之前的代码,让它变成高阶函数:
案例2:高阶函数输出“痴海666”
1def chihai(name):2    print('{}666'.format(name))34def name(func):5    func()67name(chihai('痴海'))
输出结果:
1痴海666
在案例2中我们定义了两个函数:chihai 和 name,其中 pname函数我们定义了一个 func 的参数。
这个参数我们是作为一个函数调用,也就是 chihai 这个函数,从而编写出了一个高阶函数。
在 Python 标准库中有许多的高阶函数,比如 map、filter、reduce、sorted。
3 实现一个装饰器
现在你已经知道了「函数名赋值」和「高阶函数」,有了这两个基础,我们就可以尝试实现一个类似的装饰器。
还是刚才例子,我们继续改造:实现不修改 chihai 函数的结构,多输出一句“我是痴海”。
案例3:类似装饰器实现输出“痴海666”的同时,输出“我是痴海”。

1def name(func):2    print('我是痴海')3    return func45def chihai():6    print('痴海666')78temp = name(chihai)9temp()
输出结果:
1我是痴海2痴海666
在这个例子中我们定义了一个 name 函数,name 接收一个函数名,然后直接返回该函数名。
这样我们实现了不修改原函数 chihai,并且添加了一个新功能的需求:多输出了“我是痴海”
但是这里有个缺陷就是函数的调用方式改变了。
即不是原本的 name,而是 temp。
要解决这个问题很简单,相信 a = a*3 这样的表达式大家都见过。
那么上述代码中的 temp = name(chihai),同样可以修改为:
name = name(chihai)
这样我们就完美的解决了问题:既添加新功能又没有修改原函数和其调用方式。
修改后的代码如下:
1def name(func):2    print('我是痴海')3    return func45def chihai():6    print('痴海666')78name = name(chihai)9name()
但这样的代码却有个不便之处,即每次使用这样的装饰器,我们都要写类似 name = status(name) 的代码。
程序员都是懒的,所以才有那么多高级的语法。
在 python 中为了简化这种情况,提供了一个语法糖 @。
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作。
在每个被装饰的函数上方,使用这个语法糖,就可以省掉这一句代码 name = chihai(name)。
最后的代码如下:
1def name(func):2    print('我是痴海')3    return func45@name6def chihai():7    print('痴海666')89chihai()
这样我们就弄清楚了装饰器的工作原理:
1 写一个高阶函数,即参数是函数,返回的也是函数;
2 在利用语法糖@,简化赋值操作;
案例3 最后的代码,就是一个非常简单的装饰器实现代码。
但这样的代码是有缺陷:name 函数直接返回了函数名,这样函数调用后我们就没办法做任何事情。
能看这篇文章的同学,肯定是喜欢 Python 这门语言的,但不一定有关注痴海的公众号。
所以我要在写一个函数,来模拟判断大家是否关注了痴海的公众号。
为了能判断大家是否关注我,此时我就需要在嵌套一层函数。
将实现额外功能的部分写在内层函数中,然后将这个内层函数返回即可。
这也是为什么装饰器都是嵌套函数的原因。
下面是实现了能够处理返回值的装饰器:
1def name(func): 2    def follow_state(): 3        result = func() 4        print(result) 5        return result 6    print('我是痴海') 7    return follow_state 8 9@name10def chihai():11    print('痴海666')12    return '学 Python 找痴海!我已关注!'1314chihai()
输出结果:
1我是痴海2痴海6663学 Python 找痴海!我已关注!
现在我们实现了能够处理返回值的装饰器,但可能你的名称不是叫痴海,可能叫小海、小痴,对不对。
所以我们还需要实现一版带参数的装饰器,这样就能修改我是谁:
1def name(name): 2    def judgen_follow(func): 3        def follow_state(): 4            result = func() 5            print(result) 6            return func 7        return follow_state 8    print("我是{}".format(name)) 9    return judgen_follow1011@name(name='小痴')12def chihai():13    print('痴海666')14    return '学 Python 找痴海!我已关注!'1516chihai()
输出结果:
1我是小痴2痴海6663学 Python 找痴海!我已关注!
上面的 name 是允许带参数的装饰器。
它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。
我们可以将它理解为一个含有参数的闭包。
当我们使用@name(name="小痴")调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。
@name(name="warn")等价于@judgen_follow,这样我们就实现了一个可以带参数的装饰器。
4 装饰器总结
最后我把今天重要的知识点,全部给大家一起总结出来:
1 什么是装饰器
a. 大白话总结装饰器:保护手机的手机壳
b. 专业话总结装饰器:处理函数的函数
2 装饰器有什么用
a. 增强函数的功能
3 装饰器使用场景
a. 增强被装饰函数的行为
b. 代码复用
4 装饰器理解的基础
a. Python 中的函数也是变量
b. 一个函数接收另一个函数作为参数,这种函数称之为高阶函数。
c. @符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作。
装饰器的本质是函数,其参数是另一个函数(被装饰的函数)。
装饰器通常会额外处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
行为良好的装饰器可以重用,以减少代码量。
对于一个最简单的二层装饰器,其实就是外层函数传入被装饰的函数的函数名。
内层函数要么调用这个被装饰的函数,要么返回这个被装饰的函数的函数名。
外层函数返回内层函数的函数名。
装饰的过程在内层函数中完成。
不看的原因确定内容质量低不看此公众号
(0)

相关推荐

  • 推荐8个炫酷的 Python 装饰器!

    Python 编程语言的一大优点是它把所有功能都打包到一个小包中,这些功能非常有用.许多特性可以完全改变 Python 代码的功能,这使得该语言更加灵活.如果使用得当,其中一些功能可以有效缩短编写程序 ...

  • 一文看懂Python系列之装饰器(decorator)(工作面试必读)

    Python的装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能.Python的装饰器同时也是Python学习从入门到精通过程中 ...

  • 一文搞懂Python装饰器

    一.前言 本不打算专门写文来讲装饰器的,但有不少粉丝问到了,自己查阅了一些网上的装饰器教程,发现讲的通俗易懂的不多,也有不少照搬的文章.所以我这里专门来讲一讲它. 个人在用的人工智能学习网站推荐给大家 ...

  • 第20天:Python 之装饰器

    第20天:Python 之装饰器

  • 一文理解 Python 中的变量

    " 变量让程序活起来,不再千人一面." 我们在之前的文章<Python 基本数据类型介绍>中了解了如何创建各种基本类型的数据,但是我们的例子中使用的都是"字面 ...

  • Python 中的函数装饰器和闭包

    函数装饰器可以被用于增强方法的某些行为,如果想自己实现装饰器,则必须了解闭包的概念. 装饰器的基本概念 装饰器是一个可调用对象,它的参数是另一个函数,称为被装饰函数.装饰器可以修改这个函数再将其返回, ...

  • 关于python中if __name__ == '__main__':的理解

    调试代码的时候都会写上if __name__ == '__main__':,然后写上数据进行调试,一直没有理解到这句的含义,就照搬着写,到现在才算理解到,大概说下自己的见解. 1.在python里__ ...

  • 说说在 Python 中如何实现输出指定函数运行时长的装饰器

    假设我们需要一个可以输出某个函数运行时长的装饰器. 1 基础实现 一种可能的定义方式为: 这里利用函数装饰器,在 clock(func) 函数内部定义了一个 clock(*args) 函数,定义好后直 ...

  • Python中的多态如何理解?

    Python中多态的作用 让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数. Python中多态的特点 1.只关心对象的实例方法是否同名,不关心对象所属的类型 ...

  • 如何用外行容易理解的语言解释Python中的概念?

    Python编程学习圈 2021-07-30 现如今,编程已经成为一个非常普遍的技能,很多工作中都可以用上.于是,作为程序员,经常会被周围人(比如自己的女朋友)问到关于编程是什么的问题. 今天给大家分 ...

  • python中的other.a怎么理解?

    表白:黑白圣堂血天使,天剑鬼刀阿修罗.  讲解对象:/python中的other.a怎么理解? 作者:融水公子 rsgz Python3 教程 Python3 教程 http://www.rsgz.t ...

  • 如何理解和使用 Python 中的列表

    原创 马超 DeveloperPython 2017-10-28 阅读本篇大概需要 6 分钟. 昨天写了一篇 我到底有多么努力 之后,收到很多评论和感动. 评论中大多数人都讲到自己也在努力,我也相信只 ...