python装饰器

624 阅读5分钟

装饰器作为python当中的一个重要的语法,很多朋友光靠博客理解本身就存在难度,所以推荐一个视频,作为参考学习。装饰器视频参考链接:www.bilibili.com/video/av185…

函数-->装饰器

在装饰器之前,先了解一下函数的几个核心概念。

要知道,在python中,函数也是对象。我们可以把函数赋予变量,比如说:

def  func(message):
        print('got a message: {}'.format(message))

send_message  = func
send_message('hello  world')

#输出
Got a message: hello world

这个例子中,我们把函数func()赋予了变量send_message,这样之后调用send_message,就相当于是调用函数func()

第二点,我们可以把函数当作参数,传入另一个函数中,比如说:

def  get_message(message):
        return 'Got a message: ' + message

def  root_call(func, message):
        print(func(message))

root_call(get_message, 'hello world')

#输出
Got a message: hello world

这个例子中,就把函数get_message()以参数的形式,传入了函数root_call()中然后调用它。

第三点,我们可以在函数里定义函数,也就是函数嵌套,比如说:

def  func(message):
       def  get_message(message):
              print('Got a message: {}'.format(message))
        return get_message(message)

func('hello world')

#输出
Got a message: hello world

这段代码中,我们再函数func()里又定义了新的函数get_message(),调用后作为func()的返回值返回。

第四点,函数的返回值也可以是函数对象(闭包),比如说:

def  func_closure():
        def  get_message(message):
               print('Got a message: {}'.format(message))
         return get_message

send_message = func_closure()
send_message('hello world')

#输出
Got a message: hello world

这里,函数func_closure()的返回值是函数对象get_message()本身,之后,我们将其赋予变量 send_message,再调用 send_message(‘hello world’),最后输出了'Got a message: hello world'。

简单的装饰器

我们先看一个简单的例子:

def my_decorator(func):
      def wrapper():
             print('wrapper  of  decorator')
             func()
       return wrapper

def  greet():
        print('hello world')

greet = my_decorator(greet)
greet()

#输出
wrapper of decorator
hello world

这段代码中,变量greet指向了内部函数wrapper(),而内部函数wrapper()中又会调用原函数greet(),因此,最后调用greet()时,就会先打印'wrapper of decorator',然后输出'hello world'。

其实,这里的函数my_decorator()就是一个装饰器,它把真正需要执行的函数greet()包裹在其中,并且改变了它的行为,但是原函数greet()不变。

事实上,上述代码在python中有更简单,更优雅的表示:

def  my_decorator(func):
        def wrapper():
               print('wrapper  of  decorator')
               func()
         return wrapper

@my_decorator
def  greet():
        print('hello world')

greet()

这里的@称之为语法糖,@my_decorator就相当于前面的greet=my_decorator(greet)语句,只不过更加简洁。因此,如果程序中有其他函数需要做类似的装饰,只需在它们的上方加上@decorator就可以了,这样就大大提高了函数的重复利用和程序的可读性。

带有参数的装饰器

有参数需要传递给装饰器怎么办?

一个简单的办法就是,是可以在对应的装饰器函数wrapper()上,加上相应的参数,比如:

def  my_decorator(func):
        def  wrapper(message):
                print('wrapper  of  decorator')
                func(message)
          return wrapper

@my_decorator
def greet(message):
        print(message)

greet('hello world')

# 输出wrapper of decorator
hello world

不过,还有一个问题,如果还有另外一个函数,也需要使用my_decorator()装饰器,但是这个新的函数有两个参数,比如:

@my_decorator
def celebrate(name, message):
    ...

通常情况下,我们会把*args和kwargs,作为装饰器内部函数wrapper()的参数。*args和kwargs,表示接受任意数量和类型的参数,因此装饰器就可以写成下面的形式:

def  my_decorator(func):
        def  wrapper(*args, **kwargs):
                print('wrapper  of  decorator')
                func(*args, **kwargs)
         return  wrapper

带有自定义参数的装饰器

其实,装饰器还有更大程度的灵活性。装饰器可以接受原函数任意类型和数量的参数,除此之外,它还可以接受自己定义的参数。

比如我想要定义一个参数,来表示装饰器内部函数被执行的次数,那么就可以写成下面这种形式:

def  repeat(num):
          def  my_decorator(func):
                  def wrapper(*args, **kwargs):
                         for  i  in  range(num):
                                print('wrapper  of  decorator')
                                 func(*args, **kwargs)
                    return  wrapper
             return  my_decorator

@repeat(4)
def  greet(message):
        print(message)

greet('hello  world')

# 输出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world

类装饰器

实际上,类也可以作为装饰器。类装饰器主要依赖于函数__call__(),每当你调用一个类的示例时,函数__call__()就会被执行一次。

class  Count:
         def  __init__(self,  func):
                self.func = func
                self.num_calls = 0
 
          def  __call__(self, *args, **kwargs):
                 self.num_calls  += 1
                 print('num  of  calls is: {}'.fromat(self.num_calls))
                 return  self.func(*args,  **kwargs)

@Count
def  example():
         print("hello  world")

example()

# 输出
num of calls is: 1
hello world

example()

# 输出
num of calls is: 2
hello world
...

我们定义了类Count,初始化时传入原函数func(),而__call__函数表示让变量num_calls自增1,然后打印,并且调用原函数。因此,在我们第一次调用函数example()时,num_calls的值是1,而在第二次调用时,它的值变成了2.

装饰器的嵌套

实际上,python也支持多个装饰器,比如写成下面这样的形式:

@decorator1
@decorator2
@decorator3
def func():
    ...

它的执行顺序从里到外,所以上面的语句也等效于下面这行代码:

decorator1(decorator2(decorator3(func)))

这样,上面的例子,就可以改写成下面这样:

import  functools

def  my_decorator1(func):
       @functools.wraps(func)
        def  wrapper(*args, **kwargs):
                print('execute  decorator1')
                func(*args,  **kwargs)
          return  wrapper

def  my_decorator2(func):
        @functools.wraps(func)
         def  wrapper(*args, **kwargs):
                 print('execute  decorator2')
                 func(*args,  **kwargs)
          return  wrapper

@my_decorator1
@my_decorator2
def  greet(message):
        print(message)

greet('hello world')

# 输出
execute decorator1
execute decorator2
hello world

其实,所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。