python-装饰器

Posted ckzeng-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-装饰器相关的知识,希望对你有一定的参考价值。

一.装饰器的作用

  装饰器的作用就是在不改变原有函数调用方式和代码的情况下,给这个函数增加需要的功能。
  例如:

def print_1():
    print(1)

  然后这个函数在项目的很多地方都被调用了,现在需要在不改变调用方式和函数代码的情况下,在调用print_1时先打印一下当前时间,这种时候就需要用到装饰器了。

二.装饰器的实现原理。

1.函数作为参数传给另一个函数

def print_1():
    print (1)

def print_time(func):
    print(time.time())
    func()

print_time(print_1)            #将print_1作为参数传给print_time

执行结果:

  1575794937.7757223
  1

  这里会先执行print(time.time()),再执行func(),因为传了print_1进来,这里func()就相当于print_1(),跟我们看到的结果一样

2.函数的嵌套
  python允许在函数里定义另一个函数   

def print_1():
    print (1)


def print_time(func):
    def inner():
        print(time.time())
    inner()    
    func()


print_time(print_1) 

  这里先执行的inner里的print(time.time),再执行func()

3.将函数作为返回值

def print_1():
    print (1)

def print_time(func):
    def inner():
        print(time.time())
        func()
    return inner
    
tmp=print_time(print_1)   #这里因为print_time反回inner函数,所以这一步相当于把inner函数赋值给tmp
tmp()             #然后这里tmp(),这里相当于调用了inner函数,然后就执行print(time.time())和func() 

 

  这个时候用tmp(),就完成了最初的目标每次调用print _1时都打印一下时间的效果了,但是还有个条件没满足,那就是不改变原有的调用方式,这里变成了tmp就不行了。

  接下来再进一步:

print_1 = print_time(print_1)    #这里把iprint_time返回值inner函数赋值给print_1
print_1() 

  这样就完全实现了最初的目标了,不改变函数调用方式也不修改函数代码,给print_1增加了功能。
  然后,print_1 = print_time(print_1),这样的代码实在有点难看,所以python为我们提供了语法糖神奇的@符号。
  只需要在print_1函数定义的时候加上@print_time即可,如:
    

@print_time             #这一句相当于print_1 = print_time(print_1)
def print_1():
    print(1) 

  所以,完整的装饰器实现过程如下:    

import time
from functools import wraps

#定义一个装饰器
def wrapper_print_time(func):
    @wraps(func)
    def inner():
        print(time.time())
        func()
    return inner
    
#在需要用到装饰器的函数定义前加@装饰器名
@wrapper_print_time     #相当于print_1 = wrapper_print_time(print_1)
def print_1():
    print (1)


print_1()

  @wraps(func)

  这一句不加一样能实现装饰器的效果,加这一句是为了避免一些错误, 作用是将print_1函数的一些属性赋给inner函数
  
print(print_1.__name__)    
  装饰器里不加
@wraps(func)的话,这里会打印出inner,如果加了的话则会打印出print_1

三.可以接收参数的装饰器

  现在多一个print_2函数,有个需求就是print_1和print_2函数调用时都要先打印时间,并且需要知道是哪个函数被调用了,根据被调用的函数执行不同的操作,这时候就需要给装饰器传参了

import time
from functools import wraps


# 定义一个装饰器
def firstCalled(*args, **kwargs):
    def wrapper_print_time(func):
        @wraps(func)
        def inner():
            print(args)
            print(time.time())
            func()
        return inner
    return wrapper_print_time


@firstCalled(this is print_1)  
def print_1():
    print(1)


@firstCalled(this is print_2)
def print_2():
    print(2)

print_1()
print_2()


#@firstCalled(‘this is print_1‘)这里首先等于@+firstCalled(‘this is print_1‘),
#就是先执行firstCalled(‘this is print_1‘),所以把wrapper_print_time地址传了回来
#所以就等于@wrapper_print_time就跟上面不带参数的装饰器一样了
#不同的只是firstCalled(‘this is print_1‘)调用时先把参数传进去了

 

 

 




以上是关于python-装饰器的主要内容,如果未能解决你的问题,请参考以下文章

[TimLinux] Python 装饰器

python装饰器

python装饰器关键代码

Python装饰器

python之装饰器

python 装饰器