Python的装饰器是面试的常客,因为其写法复杂多变,经常忘记什么地方应该写哪种参数,新手学习起来也经常一头雾水,不怕不怕,看了这一篇你对装饰器的各种用法就全明白了。废话不多说,直接进入主题!

不带参数的函数,不带参数的装饰器

我们先来写一个简单的装饰器,实现将函数运行前后的情况记录下来。

defdec1(func):print(func)def_wrap():print('before run')

r=func()print('after run')returnrreturn_wrap

@dec1deff1():print('call f1')

上面只是定义了两个函数,运行后发现竟然有输出:

仔细看看,原来是第一个print语句的输出。这说明装饰的函数还没有实际运行的时候,装饰器就运行过了,因为@dec1相当于单独的一个语句:

dec1(f1)

那我们来正式运行一下f1吧:

f1(1)

输出如下确实达到了预期的效果:

before run

call f1

after run

不带参数的函数,带空参数的装饰器

后面我们还想要给装饰器加上参数呢,先试试用这个方式调用会发生什么情况:

@dec1()

输出了错误:

Traceback (most recent call last) in

8 return_wrap9

---> 10@dec1()11 deff1():12 print('call f1')

TypeError: dec1() missing1 required positional argument: 'func'

它说dec1需要接受func这个参数才行,那我们改改,作为f2函数吧:

defdec2():def_wrap(func):print(func)print('before run')returnfuncreturn_wrap

@dec2()deff2():print('call f2')

f2()

这下可以了:

before run

call f2

可是这个结构和原来有点不同了,而且,after run要写在哪里呢?很愁人地又改了一版,把它叫做f2x吧,对比dec1,又多了一层函数dec2x_w,开始有点晕:

defdec2x_w():defdec2x(func):print(func)def_wrap():print('before run')

r=func()print('after run')returnrreturn_wrapreturndec2x

@dec2x_w()deff2x():print('call f2x')

f2x()

运行一下看看,确实是想要的:

before run

call f2x

after run

后面我们就不加before/after run了。

带参数的函数,不带参数的装饰器

函数f2x想要接受参数呢?我们把它叫做a吧,比较简单,不就是_wrap的参数吗,加上就是了,而且又回到了只有两层函数就可以实现了:

defdec3(func):print(func)def_wrap(param):print(param)

r=func(param)returnrreturn_wrap

@dec3deff3(a):print('call f3', a)

f3(1)#很争气地输出了结果:

1call f31

带参数的函数,带参数的装饰器

下面我们实现一个装饰器,传入一个整数,和函数传入的参数做个加法:

defdec4_w(d_param):print(d_param)defdec4(func):print(func)def_wrap(param):print(param)

r= func(param +d_param)returnrreturn_wrapreturndec4

@dec4_w(2)deff4(a):print('call f4', a)

f4(1)

输出1+2=3:2

1call f43从调用的装饰器往里看,注意这三层函数的形参,第一层是装饰器的参数,第二层是函数,第三层是函数的参数,很有规律的排列,先记一下这个规律(要考的)。带两个参数的也是一样的,接着写就可以了:defdec5_w(d_param_1, d_param_2):print(d_param_1, d_param_2)defdec5(func):print(func)def_wrap(param):print(param)

r= func(param + d_param_1 +d_param_2)returnrreturn_wrapreturndec5

@dec5_w(2, 3)deff5(a):print('call f5', a)

f5(1)

输出1+2+3=6:2 3

1call f56如果用不定数量的位置参数,就用*args作为形参吧:def dec6_w(*args):

d_param_1, d_param_2,=argsprint(d_param_1, d_param_2)defdec6(func):print(func)def _wrap(*args):

param=args[0]print(param)

r= func(param + d_param_1 +d_param_2)returnrreturn_wrapreturndec6

@dec6_w(2, 3)deff6(a):print('call f6', a)

f6(1)print(f6.__name__)

顺便输出了一下f6的函数名:2 3

1call f66_wrap

咦!怎么肥四!!!f6怎么是里面那个_wrap的名字呢?不怕不怕,functools提供了一个wraps装饰器专治各种不服(在装饰器里面放上另一个装饰器):from functools importwrapsdef dec7_w(*args):

d_param_1, d_param_2,=argsprint(d_param_1, d_param_2)defdec7(func):print(func)

@wraps(func)def _wrap(*args):

param=args[0]print(param)

r= func(param + d_param_1 +d_param_2)returnrreturn_wrapreturndec7

@dec7_w(2, 3)deff7(a):print('call f7', a)

f7(1)print(f7.__name__)

这下正常输出f7了:2 3

1call f76f7

装饰器类(带参数的函数,带参数的装饰器)

用函数做装饰器局限性太多了,用相同的调用方法,把函数f7改成类怎么样?emmm…改造工程有点大,直接看看成品吧:

from functools importwrapsclassdec8_c:def __init__(self, *args):

self.d_param_1, self.d_param_2,=argsprint(self.d_param_1, self.d_param_2)def __call__(self, func):print(func)

@wraps(func)def_wrap(param):print(param)

r= func(param + self.d_param_1 +self.d_param_2)returnrreturn_wrap

@dec8_c(2, 3)deff8(a):print('call f8', a)

f8(1)print(f8.__name__)

看看是不是实现了一样的效果:

2 3

1call f86f8

虽然使用了__call__,但这里的__init__不能省略(因为它需要知道参数个数),否则会出现这个错误:

Traceback (most recent call last) in

14 returndec815

---> 16 @dec8_c(2, 3)17 deff8(a):18 print('call f8', a)

TypeError: dec8_c() takes no arguments

同时还可以发现,__call__只需要两层函数了,去掉了第二层,直接把_wrap的函数体往上提了一层!

装饰器类(带参数的函数,不带参数的装饰器)

大概是吃饱了撑着,又想要实现一开始那个不带参数的装饰器了,那就继续敲敲打打一番看看:

classdec9_c:def __init__(self, func):print(func)

self.func=func

self.__name__ = func.__name__

def __call__(self, param):print(param)

func=self.func

r=func(param)returnr

@dec9_cdeff9(a):print('call f9', a)

f9(1)print(f9.__name__)

赶快运行看看:

1call f91f9

咦,f9的函数名可以直接打印,这下都不用@wraps了呢!呃,再仔细看看,这写法好像有些不一样啊:

dec8_c的init带的是装饰器的参数,可是dec9_c带的是装饰器函数自己!

所以实际调用的函数名也可以在init中传给它了哦!

而且call函数也简洁了很多,看来有没有参数真的有很大区别呢!

这里先做个总结,装饰器使用函数名形式(不带括号)和使用函数调用形式(带括号和参数)在实现上是不同的,因为前者是函数本身,而后者是从装饰器函数中返回的函数。这也是f2相比f1缺少了记录after run的原因,因为dec1直接调用了f2,而dec2先运行得到函数,再把函数返回去调用f2。用装饰器类就可以解决这个问题,因为它是对__call__的调用,只需要自己定义一下就可以了。

上面的f9要写两个函数,能不能写得和f1一样简洁?当然是可以的,使用__new__大法:

from functools importwrapsclassdec9x_c:def __new__(self, func):print(func)

@wraps(func)defdec9x(param):print(param)

r=func(param)returnrreturndec9x

@dec9x_cdeff9x(a):print('call f9x', a)

f9x(1)print(f9x.__name__)

这样就避开了函数调用,不用打call了(定义__call__函数),快看它来了:

1call f9x1f9x

装饰器python详解_python装饰器详解相关推荐

  1. python装饰器作用和功能_python装饰器大详解

    一.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我们要理解两点: a.在全局不能访问到局 ...

  2. python自带装饰器详解_Python装饰器详解

    引言 装饰器简单来说是我们向一个现有的已经存在的函数或对象添加新的功能,同时呢我们又不用改变他现有的结构. 为什么我们需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye( ...

  3. python描述器做权限控制_Python装饰器14-描述器

    描述器 这是Python一个重要的概念,英文名:Descriptor descriptor是对象的一个属性,只不过它存在于类的dict中并且有特殊方法get(可能还有set和__delete)而具有一 ...

  4. python 装饰器实现事件绑定_Python装饰器是怎么实现的?

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的 ① 函数特性 python中的函数特性总的来说有以下四点: 1. 函 ...

  5. python装饰器setter实现原理_python装饰器、描述符模拟源码实现

    概要 本人python理论知识远达不到传授级别,写文章主要目的是自我总结,并不能照顾所有人,请见谅,文章结尾贴有相关链接可以作为补充 全文分为三个部分装饰器理论知识.装饰器应用.装饰器延申 装饰理基础 ...

  6. python装饰器作用和功能_Python装饰器原理与用法分析

    这篇文章主要介绍了Python装饰器原理与用法,结合实例形式分析了Python装饰器的概念.原理.使用方法及相关操作注意事项,需要的朋友可以参考下 本文实例讲述了Python装饰器原理与用法.分享给大 ...

  7. python装饰器的通俗理解_python装饰器的通俗理解

    在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Py ...

  8. python装饰器模式带参数_python 装饰器模式 我的理解

    python和javascript类似, 可以把函数当作函数的返回值, 比如 def func(f): def subfunc(): print 'subfunc' return subfunc此外f ...

  9. python log函数_python装饰器的使用

    1. 装饰者模式 装饰者模式是常用的软件设计模式之一.通过此设计模式,我们能够在不修改任何底层代码情况下,给已有对象赋予新的职责.python中可以用装饰器简单地实现装饰者模式. 1.1 将函数作为参 ...

  10. python装饰器有几种_Python装饰器使用你可能不知道的几种姿势

    前言 在Python中,装饰器是一种十分强大并且好用的语法,一些重复的代码使用装饰器语法的话能够使代码更容易理解及阅读. 因此在这里简单总结了一下Python中装饰器的几种用法以及需要注意的事情. 一 ...

最新文章

  1. 阅面科技赵京雷:全面拥抱AI2.0时
  2. mysql索引如何做_5分钟,告诉你MySQL字符串怎么做索引
  3. select隐藏_数仓|几种SQL隐藏的错误,你遇到过吗?
  4. php是独立服务吗,使用Sprockets作为PHP应用程序的独立服务
  5. Bailian2981 大整数加法【大数】(POJ NOI0106-10)
  6. vue开发:前端项目模板
  7. JavaScript基础知识
  8. 开源OA项目:办公用品如何管理?
  9. The repository 'http://ppa.launchpad.net/octave/stable/ubuntu bionic Release' does not have a Releas
  10. esp32拍照传输到手机android,esp32-cam拍照上传云平台教程
  11. 【it修真院】代码生成
  12. 华为nova2自带计算机,华为Nova2 root图文教程 华为Nova2获取root权限的方法
  13. 信息安全密码学实验四:Diffie-Hellman密钥交换协议的设计与实现
  14. 邓紫棋歌曲计算机音乐数字,邓紫棋播放量最高的十首歌曲
  15. 【日常】矩阵正态分布参数检验问题
  16. 如何理解paddle.reader.xmap_readers()函数
  17. csgo跑图文件_CSGO:萌新入坑如何快速优化游戏设置?这个跑图指令也是十分实用...
  18. 上海流浪汉沈_流浪汉,木偶和Mozilla,我的天哪
  19. BitConvert
  20. CAN总线测试与汽车以太网测试的区别

热门文章

  1. ROS1 robot path tracking
  2. HTTP Cookies
  3. 机器学习入门二 ----- 机器学习术语表
  4. ThinkPHP5.0版本和ThinkPHP3.2版本的差别
  5. php文件加锁 lock_sh ,lock_ex
  6. Maven Gradle 区别
  7. My first App Encrypt Wheel is Ready to Download!
  8. 网页滚动条向下拉动奇慢的原因
  9. IE Cookie 文件格式
  10. 车间调度建模系列2|复杂车间调度问题描述