Python骚操作!教你学会装饰器!

装饰器,听名字就可以大致猜测一下,是一个可以作为装饰的东西。放在编程里面就是在实现一个函数功能的同时,再让它更丰富一点。

Python语言和其它语言的一个不同之处在于,python中的函数可以作为一个参量传入另一个函数。

装饰器是一个闭包,把一个函数当做参数返回一个替代版的函数,本质上就是一个返回函数的函数。

装饰器就是在我们需要的一个函数外面包装一个外壳,当我们希望这个函数更漂亮时,可以通过改变这个包装的样子即可,而不需要更改原函数,也可以避免出错。

看了上述的描述不知道你是否有了一个概念,下面看一个例子就明白了。

# 这是原始函数
def func1():
    print("This is a function.")

上述函数的功能是输出一句话。那么现在有了新的要求,需要在输出这句话之前先输出一行“*”号,该怎么办呢?大家肯定有主意了,这么简单,在前面再写一个输出不就好了,的确如此。那么现在再给个条件,例如有很多个像func1这样的函数都需要在前面输出一行“*”号,或者仅仅这一次调用func1的时候需要输出星号呢,又或者原本函数相当的复杂,而需要添加的效果和功能也不简单,这种情况下还要再原函数中进行修改吗?当然不,这时候就得找找更简单、安全的方法了——装饰器。

# 写一个装饰器函数,该函数功能就是在执行函数之前输出一行“*”号
def func2(func):
    def inner():
        print('************')
        func()
    return inner

def func1():
    print("This is a function.")

f = func2(func1)
f()

下面我解释一下这个装饰器,可以看到,func1是我们真正需要的函数,这时候我们想在输出这个函数时再多添加一些功能,那么我们就需要对func1这个函数进行装饰。func2是一个外部的输出函数,他的参数是func,这只是个形参,使用的时候将需要装饰的函数传入即可,当我们执行func2这个函数的时候,其内部还有一个函数inner(),但是里面inner只是创建了这个函数而没有被调用(这个大家能理解吗,就是我们在定义一个函数时,他不会自动被调用,我们使用它时需要人为的通过这个函数来调用一下),内部读取代码仅读到def inner()这里并不进去,继续向下就到了return inner,这里返回了内部的那个函数名,但也只是个名字没有被调用(函数名后面加上小括号才可以调用)。那么return返回给谁呢,就给了下面的f,用f来接收func2的return值,现在f就是return的inner。进行最后一步,调用inner,上一步我们已经知道f就是inner,调用他就加个小括号就可以了。调用了inner函数后,他的内部有func()这个函数,并且调用了,所以可以直接执行func内的语句块。func就是最外面传进来的参数,也就是我们需要被装饰的函数。小伙伴们慢慢理一下思路,一定要把第一个理清楚了,那么后面就轻而易举了。

上面这一大段话看似有些绕,但是只要照着代码,仔细琢磨一样,还是不难的。这就是装饰器的真面目。下面看一些装饰器的进阶内容,也是实用内容。

含有参数的装饰器

# 该装饰器的功能是判断输入数字是否非负,若为负,则置为零
def func2(func):
    def inner(a):
        if a < 0:
            a = 0
        func(a)
    return inner

def func1(a):
    print("the number is %d" % a)

f = func2(func1)

f(-7)

看一下,这个框架和第一个几乎一样对吧,唯独多了一个参数,这个参数是最后由我们装饰的那个函数来使用,由最外部传入。我们把参数给在了inner函数,这是为什么呢?我们想一下,我们装饰的函数是在什么时候调用的,是不是在调用了inner函数后,inner函数内自动调用的啊。那么在调用inner函数时才调用的目标函数,我们不就应该把目标函数的参数从这里传进去吗(最外层的函数是个包装的作用,他是为了返回inner函数的地址,来为之后调用inner做准备),我们在用f接收这个inner函数,我们调用他还需要加个小括号f(),这才是调用inner,既然这里调用,正好有个传入参数的接口,我们趁着这个机会把想要实现的func1函数的参数扔进去,接下来我们执行func1函数的时候,只需要把上面接收到的参数再传到自己的接口里就好了。

写成上面的形式,我们还是不满意,一个劲的调用函数,一会就懵了。神器来了,@符号是装饰器的语法糖,在定义函数时,可以避免赋值操作。

def func2(func):
    def inner(a):
        if a < 0:
            a = 0
        func(a)
    return inner

@func2
def func1(a):
    print("the number is %d" % a)

# 这时就可以直接使用原来的func1函数了,不需要引入变量来接收,就已经可以使用装饰器里的内容
func1(-7)

那么这段代码有什么作用呢?

其实它可以替换掉上方代码的后几行,代码中最开始的装饰器是一样的,我们改进在使用装饰器上。上面我们通过先定义一个目标函数,然后把它作为参数传入装饰器里,然后用一个变量f来接收,最终调用f()来实现装饰作用,这样是不是繁琐了点呢,我们做了这么多工作,而且可能会把我们搞晕,其中还引入了变量,当代码很长时,那么多的变量我们怎么记得住呢,这时就有了语法糖。大约在python2.4就开始可以使用@符号了。也许大家见过,有时候会有@staticmethod,@classmethod的字眼,这就是python的内置装饰器。

@func2的作用就是替代了上方在装饰器里传参数然后赋值的过程,可以理解为通过这样一个符号,就已经把自己变成装饰了之后的样子,那么我们再使用的时候,只需要和以往一样,调用函数,传参就可以了。当然不是真正的变了,当下方再次使用这个函数时,如果不添加语法糖还会是函数自己。

但此时的装饰器只能接收一个参数,为整形,因为内部使用了判断,这样的话我们很多事情就不是很方便了,使用装饰器就是方便我们进行后续的操作,这样的话我们使用不一样的功能还得总是修改装饰器。那么,我们继续向下看。

通用装饰器

def func2(func):
    def inner(*args,**kwargs):
        # 功能
        print("&&&&&&")
        func(*args,**kwargs)
    return inner

@func2
def func1(name,age,gender=1,number='00000000'):
    print('%s is %d years old,number is %s,gender:%d' % (name,age,number,gender))

func6('张三',18,0,'05162002')

# 函数的参数理论上是无限制的,但实际上最好不要超过6到7个

这个其实没有太多要说的,只是把参数换成了不定长参数:

  • *args:可以接受不限量个参数,将他们打包成tuple给函数

  • **kwargs:可以将关键字参数打包成字典给函数

有了上面两个,那么几乎所有的参数都可以随便输入了。

装饰器顺序问题

当功能比较多的时候,可能会碰上多个装饰器的问题,这个其实也不复杂,层层装饰即可,距离被装饰函数越近的越先装饰,依次向外。

今天网站都变成灰色了,这其中是怎么实现的?

【机器学习】朴素贝叶斯算法(Naive Bayes,NB)

【机器学习】K-近邻算法(KNN)

推荐一套高效的码字工具

python教你生成动态二维码

还在用Matplotlib? 又一可视化神器pyecharts登场

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值