资讯专栏INFORMATION COLUMN

Python的@装饰器是干什么用的?

NusterCache / 833人阅读

摘要:那么,这个装饰器要怎么定义呢我们来看一下。当然了,如果大家在网络上搜索,关于如何定义装饰器,看到的是一个更加规范的版本。在当中,调用原函数时又,即把输入的元祖解包再传入。

我们在编程过程中,常常会遇到这种需求:

比如,我想开发一款计算器,我已经写好了一堆函数,用于执行各种计算,那么我们需要在执行各种计算函数前,首先对输入的数据进行检查,确保他们必须得是数值才允许执行函数,而不能是字符串;

又如,我想编写一个用于计算三角形周长、面积、某个角角度的模块,已经写好几个函数用于计算,那么,在执行计算前,首先要确保输入的三条边长能够构成三角形,再进行计算才有意义;

再比如,我想开发某款网络应用,写了一些函数用于实现用户的某些操作,那么,得要先检查确认该用户已经登录了,才允许执行这些操作

这些需求,归纳起来,就是,在执行主函数之前,常常要先执行某个预函数,进行一些校验之类的操作。

这类需求是非常常见的,也是保证程序完整性、健壮性的重要举措。所以,怎么做才比较简单呢?

你会说,这很简单啊,在每个函数里面写上if语句不就得了。就拿那个计算器而言,如果我们要写加减乘除,我们可以这样:

def plus(a,b):    if type(a)==type(0) and type(b)==type(0): #假设该计算器只能计算整数,如果要计算小数再or type(0.0)        return a+b    else:        print("Type must be number") #检测到数据类型不对,先输出报警,函数值返回None        return Nonedef minus(a,b):    if type(a)==type(0) and type(b)==type(0):        return a-b    else:        print("Type must be number")        return Nonedef multiply(a,b):    if type(a)==type(0) and type(b)==type(0):        return a*b    else:        print("Type must be number")        return Nonedef divide(a,b):    if type(a)==type(0) and type(b)==type(0):        return a/b    else:        print("Type must be number")        return None

这个嘛,直接暴力。但是呢,这里只有4个函数,假如你开发的计算器有几十几百个函数,每个函数都要套上if语句,这不得麻烦死了,不烦死也啰嗦死了

所以怎么弄简单一点呢?聪明的你肯定想到了,我们可以把那个判断if也多带带定义一个函数,然后把计算用的函数套在里面,就像这样:

def check(a,b,func): #定义检查函数,变量为待检测参数a,b和检测通过后执行的函数func    if type(a)==type(0) and type(b)==type(0):        return func(a,b)    else:        print("Type must be number")        return Nonedef plus(a,b):    return a+bdef minus(a,b):    return a-b...#主程序check(1,2,plus) #计算1+2check(1,2,minus) #计算1-2check(1,2,multiply) #计算1*2check(1,2,divide) #计算1/2

这里面有一点一定要特别注意,主程序的check(1,2,plus) 是把plus函数本身作为变量传递给check,由check函数决定如何执行plus函数,此处不能写成check(1,2,plus(1,2)),plus不能带参数和括号,不是执行plus()后把结果传给check。

这么写程序简洁了不少,加减乘除函数只需要定义他们本身的运算就可以了,变量检测交给了check函数。这么写也是比较容易理解的。

但是对于使用该程序的用户来说,就不是这么回事了,他们会觉得这么写非常难看

为什么呢?我是要拿程序做加减乘除计算的,但我不论计算什么,每次都是在主调用check这个函数

那有没有什么办法,可以既好看,又简洁呢?装饰器就是起到了这个神奇的作用。

上面这个需求,用装饰器可以这么写:

def check(func):    ...@checkdef plus(a,b):    return a+b@checkdef minus(a,b):    return a-b...#主程序plus(1,2) #计算1+2minus(1,2) #计算1-2...

先直观感受一下,通过@check,check函数就被“注入”到了plus函数中,使得plus函数拥有了参数检测的功能。这样,在主程序中,若要计算加法就可直接调用plus,便可先校验再计算。

那么,这个装饰器check要怎么定义呢?我们来看一下。

def check(func): #定义装饰器check    def newfunc(a,b): #定义函数模板,即如何处理func        if type(a)==type(0) and type(b)==type(0):            return func(a,b)        else:            print("Type must be number!")            return None    return newfunc #将处理后的func作为新函数newfunc输出@checkdef plus(a,b):    return a+b#主程序,计算1+2plus(1,2)

我们可以看到,当装饰器@check作用于plus函数时,plus函数本身作为参数func传入装饰器中。在装饰器check的定义内部,定义了一个函数模板,描述了对输入的func如何处理。可以看到,newfunc对func(也就是输入的plus)套用了判断数据类型的if语句,最后,再将套好的newfunc输出,替代原来的func。这样,此时执行func就是在执行newfunc,执行plus就是在执行套上if语句的新函数。

所以,通过装饰器,添加上了判断语句的新函数替换了原来的plus函数,但仍通过plus这个函数名调用,所以看起来就是plus函数被“装饰”了。

当然了,如果大家在网络上搜索,关于如何定义装饰器,看到的是一个更加规范的版本。看起来更难理解一些,但其实是一样的:

def checkall(func):    def wrapper(*args,**kwargs):        if type(args[0])==type(0) and type(args[1])==type(0):            return func(*args,**kwargs)        else:            print("Type must be number!")            return None    return wrapper

模板函数一般习惯用wrapper来表示,这个没啥,建议大家都这么写,规范一些。

参数一般用不定长的*args,**kwargs来表示,这个可能有些人就困惑了。因为被装饰的函数可能有很多种,参数的个数一般也不确定。然后*args,**kwargs是什么东西?args,kwargs这两个形参英文字母是什么无所谓可以自己定,关键是前面的单星号*和双星号**。

假如我定义一个函数,不能确定参数有多少个,例如要对输入的一组数字做连加操作。那么就可以定义plus(*x),当调用该函数时,若输入多个变量plus(1,2,3),那么就会把输入的变量组合成一个元祖x=(1,2,3)输入。定义双星号plus(**x)的意思是,调用该函数时若写出形参变量plus(a=1,b=2,c=3),那么输入变量就会组合成字典x={a:1,b:2,c:3}传入函数。

当然也可以反向操作,定义函数的时候参数个数是明确的plus(a,b,c),那么调用该函数时,加上星号plus(*(1,2,3)),就是对输入元祖(1,2,3)执行炸开操作,转换为plus(1,2,3)输入。

装饰器里这么写有什么用呢?我们仔细观察一下我们之前写的newfunc(a,b),那就意味着,指明了新函数有两个参数a,b,假如被装饰的原函数有三个参数怎么办呢?不就没用了吗?

我们来看别人写的,定义时用了wrapper(*args,**kwargs),即不管有多少个参数,打包输入wrapper。在wrapper当中,调用原函数时又func(*args,**kwargs),即把输入的元祖解包再传入func。这么一打包一解包,虽然看起来啥都没干,但确适应了函数参数不确定的情况,使得该装饰器可以装饰多种参数数量不同的函数

就先这样吧。


技术交流

欢迎转载、收藏、有所收获点赞支持一下!

目前开通了技术交流群,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友

  • 方式①、发送如下图片至微信,长按识别,后台回复:加群;
  • 方式②、添加微信号:dkl88191,备注:来自CSDN
  • 方式③、微信搜索公众号:Python学习与数据挖掘,后台回复:加群

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/124520.html

相关文章

  • Python装饰

    摘要:一引用书流畅的书二基本概念问题装饰器是什么解答严格来说,装饰器只是语法糖,装饰器是可调用的对象,可以像常规的可调用对象那样调用,特殊的地方是装饰器的参数是一个函数问题装饰器有什么特性解答装饰器有个特性,一是可以把被装饰的函数替换成其他函数, 一, 引用 [书] 流畅的Python [书] Effective Python 二, 基本概念 showImg(https://segme...

    aisuhua 评论0 收藏0
  • 利用世界杯,读懂 Python 装饰

    摘要:今天就结合最近的世界杯带大家理解下装饰器。而德国是上届的冠军,又是这届夺冠热门。装饰器的存在是为了适用两个场景,一个是增强被装饰函数的行为,另一个是代码重用。在利用语法糖,简化赋值操作。行为良好的装饰器可以重用,以减少代码量。 Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性,熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。 show...

    xiguadada 评论0 收藏0
  • python装饰原理和使用

    摘要:我们以测量函数运行时间为例来讲一讲装饰器的运行原理。三更加通用的装饰器前面两部分讲了装饰器的原理,这一部分就讲讲要写出一个通用的装饰器需要注意的问题。首先就是参数的问题,装饰器返回的函数不是原来的函数,函数的签名也就和原来的函数签名不一样。 一、最简单的装饰器 装饰器是python中很基础也很实用的一个特性。通过装饰器我们可以很方便地为一些函数添加相同的功能。我们以测量函数运行时间为例...

    goji 评论0 收藏0
  • Python知识点:理解和使用装饰器 @decorator

    摘要:使用类装饰器,优点是灵活性大,高内聚,封装性。不过不用担心,有,本身也是一个装饰器,它的作用就是把原函数的元信息拷贝到装饰器函数中,使得装饰器函数也有和原函数一样的元信息。 showImg(https://segmentfault.com/img/bVbrFWb?w=742&h=484);Python的装饰器(decorator)是一个很棒的机制,也是熟练运用Python的必杀技之一。...

    cyqian 评论0 收藏0
  • Python 装饰器使用指南

    摘要:装饰器是可调用的对象,其参数是另一个函数被装饰的函数。第二大特性是,装饰器在加载模块时立即执行。另一个常见的装饰器是,它的作用是协助构建行为良好的装饰器。 装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 装饰器基础知识 首先看一下这段代码 def deco(fn): print I am %s! % fn.__name__ @deco def func(): ...

    NeverSayNever 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<