美文网首页
Python-装饰器

Python-装饰器

作者: 张氏小毛驴 | 来源:发表于2018-05-16 21:31 被阅读0次

说到装饰器,很明显就是用来装饰的,既然是要装饰,那肯定是在保留原有的基础上再添加一些东西作为装饰,这就是我对装饰器最直白的理解。

那么如何去学习这个装饰器呢?这个装饰器又是咋回事?

装饰器的几个特点


首先我们需要先记住装饰器的几个特点:

  • 装饰器本质上也是一个函数 ,而这个函数的作用就是给其他函数添加一些其他功能作为装饰。
  • 装饰器不能去修改原函数的源代码,只能是增加修饰
  • 装饰器不能去修改原函数的调用方式 ,比如函数fun(),调用方式就是fun(),不能改变。

怎么实现装饰器


装饰器如果往简单的说就是以前学习的几个知识点结合组合在一起。

装饰器= 高阶函数+嵌套函数

高阶函数


  • 将函数名作为参数传递给另一个函数

    在python里,函数也是可以像变量那样直接赋值给另一个变量的,这样子就可以把函数名作为实参传给另一个函数了。(依据这个就可以保证装饰器特点之一:不修改原函数的源代码,只添加功能)

    def func():
        print("Hello I am func")
        
    def main(var):
        print("Before var()")
        var()
        print("End var()")
        
    #把func作为参数传给main
    main(func)
    # 这样子就给func()函数执行前和执行后都添加了一句打印,是不是没有修改了源码,只添加了功能?
    
  • 返回值包含函数名

    函数名字可以作为参数传递,那么自然函数名也可以作为返回值了。

    在上面的例子中,虽然没有修改源码就在函数执行前后添加了一句打印,但是这时候函数的调用方式变为了main(func) ,而原函数的调用方式应该是func() ,这就与装饰器的另一个特点:不能修改原函数的调用方式冲突了,解决方式就是函数名作为返回值。

    def func():
        print("Hello I am func")
        
    def main(var):
        print(var)
        return var
    
    print(main(func)) #执行结果:会先打印func的内存地址,然后因为return的也是func,所以也会打印出func的内存地址,也就是会打印两次func的地址
    
    #也可以把返回值赋值给另一个变量
    test = main(func)  
    test()  #等同于func()
    
    #为了不改变调用方式,我可以这么做:
    func = main(func)
    func()  #这样子就跟直接调用func(),看起来一样了,调用方式没有改变
    

嵌套函数


嵌套函数,就是之前讲过的内部函数,在函数内部定义另一个函数,这里就当做温习下,这里注意内部函数调用和没调用的差别,整体理解不算难。

# 内部函数只定义,没有调用
def out():
    print("I am out")
    def _in():
        print("I am _in")
       
# 只调用out()
out()   # 只会执行out(),内部函数_in()不会执行,因为内部只定义并没有调用

# 内部函数即定义又调用
def out1():
    print("I am out1")
    def _in1():
        print("I am _in1")
    return _in1

func = out1();
func()

装饰器


1.有了高阶函数和嵌套函数的基础,对于实现一个装饰器就显得比较好办了,从上面例子看好像高阶函数就能实现了装饰器的两个特点,但其调用方式太绕,而高阶函数又恰好能解决这个,两者结合就能实现装饰器了。

# 定义一个装饰器
def dec(func):
    # 内部定义函数
    def _in():
        print("Before func")
        func()
        print("End func")
    return _in

# 定义一个原函数
def test():
    print("I am test()")
    
test()   # 最初的调用
test = dec(test) # 把返回值赋值给test,显得调用方式并没有变化
test()

上面例子dec就是一个装饰器,但是我们在调用的时候总是需要把返回值赋值给原函数名的变量,这样略显麻烦,在python里,有一个装饰器的语法糖,就是在定义需要装饰的函数前面用@ + 装饰器名字,比如上例子中可以改为:

# 定义一个装饰器
def dec(func):
    # 内部定义函数
    def _in():
        print("Before func")
        func()
        print("End func")
    return _in

# 定义一个原函数
@dec
def test():
    print("I am test()")
    
test()

2.有没有发现上面的例子中,原函数都是没有带参数的?下面说说带参数的原函数,装饰器怎么写。

说下思路:在装饰器里,调用原函数的地方是在内部函数里,return返回的也是内部函数的地址,那么执行的时候也是执行了内部函数,所以如果原函数有参数,那么内部函数也应该要相应跟着参数才可以,否则一定会出错,参数不匹配了。

# 函数参数个数是有限的
def dec(func):
    def _in(arg):
        print("Before func")
        func(arg)
        print("End func")
    return _in

@dec
def test(a):
    print("the arg is %d" %a)
    
test(4)

# 参数个数不限 这里就需要用到 *args **kwargs 了
def dec1(func1):
    def _in1(*args,**kwargs):
        print("Before func1")
        func1(*args,**kwargs)
        print("End func1")
    return _in1

@dec1
def test1(name,arg):
    print("The name is %s,arg is %d" %(name,arg))
   
test1("Test",12)

注意,如果一个原函数中,有多个装饰器,那么装饰器的执行顺序是怎样的呢?

答案是从靠近函数头的开始执行,依次向上,比如:

@dec2

@dec1

def func():

​ pass

执行顺序是从里到外,依次向上,等效于func = dec2(dec1(func))

3.原函数可以有参数,有没有想过装饰器也可以有参数的?很简单嘛,本文开头就说了装饰器本质就是函数,那它肯定也可以有函数啦!装饰器带参数的做法,就是再多嵌套一层函数

# 不带参数的装饰器
def dec(func):
    def _in(*args,**kwargs):
        print("I am _in")
        func(*args,**kwargs)
    return _in
   
# 带参数的装饰器
def dec1(flag = 0):
    def real_dec(func):
        def _in(*args,**kwargs):
            if flag == 0:
                print("Run func")
                func(*args,**kwargs)
            else:
                print("No Run func")
        return _in
    return real_dec

@dec
def test():
    print("I am test")
    
@dec1(1)
def test1():
    print("I am test1")

test()
print("-------------")
test1()

注意:带参数的装饰器,在装饰函数的时候要带上(),比如上面的@dec1()

另外,除了函数带参数,函数还有返回值,在装饰器里也是一样的,原函数需要返回值,那么在内部函数里就需要return。

4.原函数带有return的情况

def dec(func):
    def _in(*args,**kwargs):
        print("I am _in")
        ret = func(*args,**kwargs)
        print("ret = %d" %(ret))
        return ret
    return _in

@dec
def test(a,b): #两个数相加
    return a+b

test(3,4)

相关文章

  • python-装饰器

    @logindef search():...等价于search = login(search)

  • python-装饰器

    前言 行为:装饰器(decorator)可以对一个函数、方法或者类进行“加工”,相当于在封装。目的:抽象化代码,利...

  • Python-装饰器

    打印结果:第一次没有包装过的打印是-10第二次包装过打印 0; 以上方法可以进行优化: 利用“@” 如下 在fun...

  • Python-装饰器

    说到装饰器,很明显就是用来装饰的,既然是要装饰,那肯定是在保留原有的基础上再添加一些东西作为装饰,这就是我对装饰器...

  • python-装饰器

    基本使用 fun() 执行fun函数fun 表示函数的地址改代码原则,不要动原来代码,可以往外面加代码。也别删除代...

  • Python-装饰器

    以装饰一个函数为例子, 打印出函数的开始执行时间。 常规方式实现: 输出: 装饰器方式实现: 输出:

  • Python-装饰器

    之前写Flask应用的时候就用过,但是无奈那时候水平太低,还没有研究到这里。最近我就决定把Python的这些高级概...

  • Python-装饰器

    一、前言 python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰...

  • python-装饰器

    什么是装饰器? 我们先了解一下基本概念:它的本质就是一个函数,可以让其他函数在不需要做任何代码变动的前提下增...

  • Python-装饰器

    在目标函数前后添加功能,但又不修改目标函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Deco...

网友评论

      本文标题:Python-装饰器

      本文链接:https://www.haomeiwen.com/subject/zhimdftx.html