美文网首页
python学习:装饰器

python学习:装饰器

作者: 倔犟的贝壳 | 来源:发表于2021-11-06 15:58 被阅读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
    

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

    相关文章

      网友评论

          本文标题:python学习:装饰器

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