Python装饰器简单来讲就是用于扩展函数功能的一种工具,在扩展功能方面其与类的继承有些相似,都是为了提高代码的复用性。举个例子就是把孙悟空塞入炼丹炉,然后就出来一个还是会吃桃子会耍金箍棒,但是有火眼金睛的猴哥,只是多了特效技能,这就是为什么叫做装饰器的原因。与类的继承相比,其用途有所区别,并且降低了代码的耦合性(即类与类之间乱七八糟的关系导致整个代码体系臃肿的情形)。

Python装饰器的应用场景有很多,其中一个很普遍的功能就是打印日志或者运行时间。就比如说,如果我们需要对多个函数统计运行时间,不使用装饰器的话,会是这样的,如下:

def fun_one():start = time()sleep(1)end = time()cost_time = end - startprint(f"func one run time {cost_time}")def fun_two():start = time()sleep(1)end = time()cost_time = end - startprint(f"func two run time {cost_time}")def fun_three():start = time()sleep(1)end = time()cost_time = end - startprint(f"func three run time {cost_time}")fun_one()
fun_two()
fun_three()

在每个函数中我们的都需要获取开始时间start、结束时间end等,然后输出打印。但是如果使用装饰器的话将会是这样的,如下:

def run_time(name='one'):def time_decorator(func):def wrapper():start = time()func()                  # 函数在这里运行end = time()cost_time = end - startprint(f"func {name} run time {cost_time}")return wrapperreturn time_decorator@run_time(name='one')
def fun_one():sleep(1)@run_time(name='two')
def fun_two():sleep(1)@run_time(name='three')
def fun_three():sleep(1)fun_one()
fun_two()
fun_three()

我们先不必管这个装饰器是怎么写出来的,但是读者们应该可以清晰地看到通过创建一个装饰器run_time,省去了很多重复的内容。如果更多的函数需要统计运行时间,那么节省的内容将会更多。

1. 知识铺垫

回归正题,为了更好地理解装饰器,我们必须牢牢记住以下三点(装饰器可能会颠覆Python初学者的三观,所以为了不显得那么突兀,我们需要一步一步地来刷新读者的认知XD):

  • Python函数也可以看做对象
  • Python函数能够作为一个参数传给另外一个函数
  • 能够从函数中返回另一个函数

下面开始逐一解释这三点,注意为了搞点语言艺术,笔者取了三个不太恰当的标题,还请读者不要太过当真。

1.1 一切皆可对象

前面讲到第一点,Python函数其实也可以看做对象,如下:

def shout(text):return text.upper()
print(shout('Hello'))
# 输出:HELLO
yell = shout #  创建了一个名为yell的指针指向了shout
print(yell('Hello'))
# 输出同样为:HELLO
del shout # 删除shout对象,但是函数主体并没有删除
print(shout('Hello'))
# 输出:NameError: name 'shout' is not defined
print(yell('Hello'))
# 输出:HELLO

我们首先定义了一个shout函数,这个函数其实是可以作为对象的,即我们可以创建一个名为yell的指针指向这个shout。并且即使我们删除了原来的shout,yell指向的函数主体(即整个函数的内容)依然会打印相同的输出,如下图所示

1.2 神马都是参数

我要讲的第二点就是,Python函数其实是可以作为参数的,如下:

def shout(text):return text.upper()def whisper(text):return text.lower()def greet(func):greeting = func("I am created by a function passed as an argument.")print(greeting)greet(shout)
# 输出:I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
greet(whisper)
# 输出:i am created by a function passed as an argument.

可以看到,shout和whisper函数都分别作为了greet的参数传递了进去。

1.3 从函数中来,从函数中去

第三点就是在Python中并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来,如下:

def greet(name='shout'):def shout(text):return text.upper()def whisper(text):return text.lower()if name == "shout":return shoutelse:return whisper
a = greet(name='shout')
print(a)
# 输出:<function greet.<locals>.shout at 0x7fb1517595e0>
b = greet(name='whisper')
print(b)
# 输出:<function greet.<locals>.whisper at 0x7fb151759700>
print(a('Hello'))
# 输出:HELLO
print(b('Hello'))
# 输出:hello

首先可以看到greet返回的a和b分别指向了shout和whisper函数,再回头看看1.1所讲的,这里面的a和shout的关系其实跟1.1中的yell和shout是一样的,都是等同的对象,因此我们同样可以通过a来实现shout函数的功能,b也是同理。

2. 装饰器

铺垫了这么久,我们终于可以来讲讲装饰器的一些常见用法了,下面同样分点来讲述。

2.1 第一个装饰器

如下,我们使用@来表示装饰想要的函数,其中first_decorator是装饰器,inner是装饰函数,而func则是被装饰的函数,需要作为参数输入到装饰器中。

def first_decorator(func):def inner():print("Hello, this is before function execution")func()print("This is after function execution")return inner
def my_func_1():print("This is inside the function1 !")
my_func_1 = first_decorator(my_func_1)
my_func_1()
# 输出:Hello, this is before function execution
# This is inside the function1 !
# This is after function execution
@first_decorator
def my_func_2():print("This is inside the function2!")
my_func_2()
# 输出:Hello, this is before function execution
# This is inside the function1 !
# This is after function execution
print(my_func_2.__name__)
# 输出:inner

可以看到my_func_1 = first_decorator(my_func_1)这一行和@first_decorator是等价的。从print(my_func_2.__name__)这一行可以看出这里inner函数会跟装饰后的my_func_2函数是等价的,而inner里面的func是跟装饰前的my_func_2函数等价。

2.2 装饰器的输入与输出

在2.1节中,装饰器整个结构包括装饰器本身first_decorator、装饰函数inner和被装饰函数my_func_2,其中装饰器的输入就是被装饰函数的形参func,一般没有输出也就是return,而装饰函数inner和装饰形参func以及被装饰函数my_func_2的输入一般需要保持一致,但是一般被装饰函数my_func_2的输入参数不是确定的,所以装饰函数inner和装饰形参func的输入一般写成(*args, **kwargs)这样的形式,被装饰函数的输入则根据实际情况写就好,如下:

def first_decorator(func):def inner(*args, **kwargs):print("Hello, this is before function execution")func(*args, **kwargs)print(func(*args, **kwargs))print("This is after function execution")return "Outside returns"return inner
def my_func_1():print("This is inside the function1 !")@first_decorator
def my_func_2(text):print("This is inside the function2!")text = "Inside returns"return text
my_func_2('text2')
print(my_func_2.__name__)
print(my_func_2('text2'))

对于输出,装饰器的输出一般就是装饰函数inner,而inner的输出即return就是装饰后my_func_2的输出,my_func_2里面的return就是装饰前的return,包含在inner函数里面来利用,上述整个代码打印的输出如下,读者们可以自行琢磨:

Hello, this is before function execution
This is inside the function2!
This is inside the function2!
Inside returns
This is after function execution
inner
Hello, this is before function execution
This is inside the function2!
This is inside the function2!
Inside returns
This is after function execution
Outside returns

2.3 嵌套装饰器

嵌套装饰器的英文是Chaining Decorators,可能笔者翻译有误,但它实际上确实很像嵌套的方式,这里读者可以结合前面讲述的,研究以下例子。我们写两个装饰器,如下:

def star(func):def inner(*args, **kwargs):print("*" * 30)func(*args, **kwargs)print("*" * 30)return inner
def percent(func):def inner(*args, **kwargs):print("%" * 30)func(*args, **kwargs)print("%" * 30)return inner
@star
@percent
def printer(msg):print(msg)
printer("Hello")

这样子的输出会是:

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

但是如果装饰器star和装饰器percent的顺序反过来,如下:

@percent
@star
def printer(msg):print(msg)

其输出会是:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
Hello
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

也就是说它会首先执行被装饰函数,然后执行最靠近被装饰函数的装饰器,然后依次往上执行。但是此时代码的输出也很有趣,明明最先执行被装饰函数printer,但是打印的Hello却不是在最上面,这是因为他的输出逻辑就是嵌套的。对于第一种情况先执行percent装饰器,它首先会是如下的效果:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

然后执行star装饰器,这时候star里面的func(*args, **kwargs)相当于装饰了percent之后的printer函数,也就是在此基础上在最外面再套一层星号,这就是装饰的精髓XD。

Python装饰器通俗理解相关推荐

  1. python装饰器-如何理解Python装饰器?

    我从以下几点,由浅入深详细讲解一下Python装饰器:什么事装饰器? 为什么用装饰器? 在哪里用装饰器? 然后以示例+讲解相结合的方式阐述,同时会讲解一些在很多教程和书籍中不会涉及到的内容. 什么是P ...

  2. python装饰器哪个好_[Python] 对 Python 装饰器的理解心得

    最近写一个py脚本来整理电脑中的文档,其中需要检校输入的字符,为了不使代码冗长,想到使用装饰器. 上网搜索有关python的装饰器学习文档,主要看的是AstralWind的一篇博文,以及Limodou ...

  3. python装饰器简单理解_python装饰器的简单理解

    如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 装饰器的使用方法很固定: 先定义一个装饰函数(帽子)(也可以用类.偏函数实现) 再定义你 ...

  4. python装饰器作用-理解python中的装饰器

    一 什么是装饰器? 正如其名,装饰器的作用是为已经存在的对象增加额外功能(装饰),由此可使已有函数在无需代码改动的情况下增加额外功能:装饰器的本质是嵌套的函数且返回函数对象,即闭包.有关闭包的概念,可 ...

  5. python装饰器副作用_对Python 装饰器的理解心得

    0x00000000030A40D0位置(这个位置应该是指内存位置)存有一个function(方法)叫target:在 target后面加上(),表示调用该方法,即输入target(),"大 ...

  6. python装饰器是什么意思_对Python装饰器的理解

    想要弄明白装饰器是什么东西,首先我们需要了解一下什么是闭包,因为装饰器是闭包的一种应用. 闭包 闭包的定义: ​通俗的来说闭包就是在一个函数内部定义另外一个函数,这个函数又引用了外部函数的变量,并且外 ...

  7. python装饰器简单理解

    装饰器定义:1.把一个函数名当作实参传给另外一个函数(在不修改装饰函数源代码的情况下为其添加功能) 2.返回值中包含函数名(不改变函数调用方式) 源代码: 1 def f(): 2 def f(): ...

  8. python装饰器简单理解的小demo

    def multi_100(func):def demo(ls):# 这个函数是让列表前两个元素*100for i in range(2):ls[i] = (ls[i] * 100)func(ls)r ...

  9. python 装饰器粗浅理解

    如下是一段测试代码 #!/usr/bin/pythondef fun1(foo):print("afsaf")def fun3(): print("init...&quo ...

最新文章

  1. pandas使用query函数基于判断条件获得dataframe中满足条件的数据行(row)的索引列表(index of rows matching conditions in dataframe)
  2. java springcloud版b2b2c社交电商spring cloud分布式微服务(十三)断路器聚合监控(Hystrix Turbine)...
  3. linux中软件包安装(rpm和yum)
  4. python 周末大作业之2
  5. Hibernate 双向一对一实现(基于annotation)
  6. 当你「ping 一下」的时候,你知道它背后的逻辑吗?
  7. caffe学习(六):使用python调用训练好的模型来分类(Ubuntu)
  8. 闵可夫斯基和(Mincowsky sum)
  9. Python框架篇之Django(ORM对象关系映射)
  10. vector性能调优之resize与reserve
  11. 红帽linux网络yum源,RedHat系统使用yum网络源
  12. 观CSDN网站小Bug有感
  13. 无线通信设备安装工程概预算编制_建筑安装工程,预算编制中易遗漏总结分享...
  14. 人声歌姬语音合成器+拓展-Yamaha Vocaloid 5.0.3 + Libraries WiN 免安装版
  15. CatBoost快速入门
  16. 怎么计算机械设备使用费用,机械设备制造成本的核算方法.doc
  17. 获取某网站在alexa上的排名
  18. python研究背景和意义_选题背景、目的及研究意义
  19. 分类效果评价(机器学习)
  20. 工业软件国产化路在何方?INTEWELL助力民族工业落地生“根”

热门文章

  1. GridView單行編輯保存
  2. html保护环境主题,有关保护环境主题的手抄报内容
  3. wpf dataGrid 实现单行某个数据变化 ui 界面随之响应
  4. UC浏览器闪退问题分析报告
  5. mysql utc timestamp_Mysql Timestamp
  6. HTML鼠标滑过特效教程
  7. Andriod-解决签名错误导致wifi 扫描不到ap
  8. 个人知识管理(PKM)简介
  9. 百度只为营销而存在?
  10. 达梦数据库综合监控方案