当前位置 博文首页 > python 装饰器的基本使用

    python 装饰器的基本使用

    作者:changhao 时间:2021-02-10 18:26

    知识点

    • 简单的装饰器
    • 带有参数的装饰器
    • 带有自定义参数的装饰器
    • 类装饰器
    • 装饰器嵌套
    • @functools.wrap装饰器使用

    基础使用

    简单的装饰器

    def my_decorator(func):
      def wrapper():
        print('wrapper of decorator')
        func()
      return wrapper()
    
    
    def test():
      print('test done.')
    
    test = my_decorator(test)
    test
    
    输出:
    wrapper of decorator
    test done.

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

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

    上述代码在Python中更简单、更优雅的表示:

    def my_decorator(func):
      def wrapper():
        print('wrapper of decorator')
        func()
      return wrapper()
    
    @my_decorator
    def test():
      print('test done.')
    
    test

    这里的@, 我们称为语法糖,@my_decorator就相当于前面的test=my_decorator(test)语句

    如果程序中又其他函数需要类似装饰,只需要加上@decorator就可以,提高函数的重复利用和程序可读性

    带有参数的装饰器

    def args_decorator(func):
      def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
      return wrapper
    
    @args_decorator
    def identity(name, message):
      print('identity done.')
      print(name, message)
    
    identity('changhao', 'hello')
    
    输出:
    wrapper of decorator
    identity done.
    changhao hello

    通常情况下,会把args和*kwargs,作为装饰器内部函数wrapper()的参数。 表示接受任意数量和类型的参数

    带有自定义参数的装饰器

    定义一个参数,表示装饰器内部函数被执行的次数,可以写成这个形式:

    def repeat(num):
      def my_decorator(func):
        def wrapper(*args, **kwargs):
          for i in range(num):
            func(*args, **kwargs)
        return wrapper
      return my_decorator
    
    
    @repeat(3)
    def showname(message):
      print(message)
    
    showname('changhao')
    
    输出:
    changhao
    changhao
    changhao

    类装饰器

    类也可以作装饰器,类装饰器主要依赖于函数 __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(self.num_calls))
        return self.func(*args, **kwargs)
    
    
    @Count
    def example():
      print('example done.')
    
    example()
    example()
    
    输出:
    num of calls is: 1
    example done.
    num of calls is: 2
    example done.

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

    装饰器的嵌套

    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 test2(message):
      print(message)
    
    
    test2('changhao')
    
    输出:
    execute decorator1
    execute decorator2
    changhao

    类装饰器

    类也可以作装饰器,类装饰器主要依赖于函数 __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(self.num_calls))
        return self.func(*args, **kwargs)
    
    
    @Count
    def example():
      print('example done.')
    
    example()
    example()
    
    输出:
    num of calls is: 1
    example done.
    num of calls is: 2
    example done.

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

    装饰器的嵌套

    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 test2(message):
      print(message)
    
    
    test2('changhao')
    
    输出:
    execute decorator1
    execute decorator2
    changhao

    @functools.wrap装饰器使用

    import functools
    def my_decorator(func):
      @functools.wraps(func)
      def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
        return wrapper
    
    @my_decorator
    def test3(message):
      print(message)
    
    test3.__name__ 
    
    输出
    test3

    通常使用内置的装饰器@functools.wrap,他会保留原函数的元信息(也就是将原函数的元信息,拷贝到对应的装饰器里)

    装饰器用法实例

    身份认证

    import functools
    
    def authenticate(func):
     @functools.wraps(func)
     def wrapper(*args, **kwargs):
      request = args[0]
      if check_user_logged_in(request):
       return func(*args, **kwargs)
        else:
       raise Exception('Authentication failed')
      return wrapper
    
    @authenticate
    def post_comment(request):
     pass

    这段代码中,定义了装饰器authenticate;而函数post_comment(),则表示发表用户对某篇文章的评论。每次调用这个函数前,都会检查用户是否处于登录状态,如果是登录状态,则允许这项操作;如果没有登录,则不允许。

    日志记录

    import time
    import functools
    
    def log_execution_time(func):
     @functools.wraps(func)
     def wrapper(*args, **kwargs):
      start = time.perf_counter()
      res = func(*args, **kwargs)
      end = time.perf_counter()
      print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
      return wrapper
    
    @log_execution_time
    def calculate_similarity(times):
     pass

    这里装饰器log_execution_time记录某个函数的运行时间,并返回其执行结果。如果你想计算任何函数的执行时间,在这个函数上方加上@log_execution_time即可。

    总结

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

    js
    下一篇:没有了