python学习:装饰器

2021-11-06  本文已影响0人  倔犟的贝壳

在python中,函数也是对象,我们可以把函数作为另外一个函数的返回值(也就是闭包),也可以在函数里面嵌套函数。如下:

def func_closure():
    print("func_closure")
    def get_message(message):
        print("got a message:{}".format(message))
    return get_message

send_message = func_closure()
send_message("hello world")
#输出
func_closure
got a message:hello world

func_closure()返回了get_message函数对象,并把它赋值给send_message变量,这样,send_message就相当于get_message函数了,可以像get_message()那样使用。

装饰器

装饰器将额外增加的功能,封装在自己的装饰器函数或类中;如果你想要调用它,只需要在原函数的顶部,加上 @decorator 即可。显然,这样做可以让你的代码得到高度的抽象、分离与简化。

看一个简单的装饰器

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

上述中,函数 my_decorator() 就是一个装饰器,它把真正需要执行的函数 greet() 用wrapper包裹在其中,并且改变了它的行为,但是原函数 greet() 不变。还有更简洁的表述方法,即使用@decorator:

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")

如上,如果想要在函数前后增加额外的功能,将额外增加的功能放在修饰器中,在原函数上顶部加上@your_decorator即可。

假如我们之前是可以随便评论

#原来的提交评论
def commit_comment(text):
    print("comment is:{}".format(text))
#输出
#comment is:柯南更新了!

现在需要在评论前判断是否已登录,使用装饰器

def auth(func):
    def wrapper(text):
        print("Check if the user is logged in")
        func(text)
    return wrapper

#原来的提交评论
@auth
def commit_comment(text):
    print("comment is:{}".format(text))

commit_comment("柯南更新了!")

把检查用户登录写成一个装饰器auth,在现在需要检查用户登录的函数的顶部加上@auth即可。完全无需改动原有的代码逻辑,既简洁又方便。

装饰器的作用与意义,在于其可以通过自定义的函数或类,在不改变原函数的基础上,改变原函数的一些功能。

装饰器wrapper的参数

上面我们已经有一个不传参数的和一个传参数的装饰器。如果一个装饰器要用在几个不同的且参数个数也不同的函数上呢,参数该怎么写呢?
通常情况下,我们会把*args和**kwargs,作为装饰器内部函数 wrapper() 的参数。*args和**kwargs,表示接受任意数量和类型的参数,因此装饰器就可以写成下面的形式:

import functools
def my_decorator(func):
  @functools.wraps(func) #使方法名保持原有的方法名
  def wrapper(*args,**kwargs):
      print("wrapper of decorator")
      func(*args,**kwargs)
  return wrapper

注意上面的@functools.wraps(func),它能够使方法名保持原有的方法名,如果不加这一行的话,那方法的方法名就变成了wrapper

类装饰器

不仅函数可以做成装饰器,类也可以写成装饰器。类装饰器主要依赖于函数__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: {}'.format(id(self),self.num_calls))
        return self.func(*args, **kwargs)

@Count
def example(message):
    print("message is:{}".format(message))
@Count
def example2(number):
    print("number is:{}".format(number))
    
example("hello world")
example("welcome to shenzhen")
print('-------- 分割线------')
example2(1)
example2(2)

#输出
140414128260528 num of calls is: 1
message is:hello world
140414128260528 num of calls is: 2
message is:welcome to shenzhen
-------- 分割线------
140414128262688 num of calls is: 1
number is:1
140414128262688 num of calls is: 2
number is:2

从上面的例子中可以看到,类装饰器对于同一个函数,只会实例化一次。如果你输出type(example),你会发现,它的类型是Count。在函数定义的时候,它就初始化了成了Count的实例,后面每一次调用其实是Count实例的__call__函数的调用。

装饰器的嵌套

装饰器还可以嵌套,一个函数可以有多个装饰器装饰

@decorator1
@decorator2
def func(): pass

执行顺序是:decorator1->decorator2->func()

Classes can also be decorated: just like when decorating functions
官方文档上说,类也可以被装饰器装饰,如下:

@f1
class Foo: pass

但目前暂时还没想到类装饰器怎么用。

上一篇下一篇

猜你喜欢

热点阅读