欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python装饰器进阶

程序员文章站 2022-03-06 10:16:56
[TOC] 装饰器进阶 通过上一篇已经知道, 如果还有概念还不理解的请返回装饰器入门 "装饰器入门" 装饰器 实际也是一种函数, 他是 python语言的重要组成部分, 在实际开发中应用也相当广泛. 正如我们所知, 前面所看到的被装饰的函数都是最简单函数格式, 这里所说的简单, 是指 ,`或者函数没 ......

目录

装饰器进阶

通过上一篇已经知道, 如果还有概念还不理解的请返回装饰器入门装饰器 decorator实际也是一种函数, 他是 python语言的重要组成部分, 在实际开发中应用也相当广泛. 正如我们所知, 前面所看到的被装饰的函数都是最简单函数格式, 这里所说的简单, 是指 函数无参数的形式,或者函数没有返回(直接执行结果)值在实际开发中我们遇到更多的其实是拥有多个参数`,那么这种装饰器该如何编写?

被装饰的函数有参数

  • 实例代码

    from functools import wraps
    
    def auth2(func):
        @wraps(func)
        def wrapper(*arg, **kwargs):
            user = input("pls input password:").strip()
            if user == "faily":
                print("---welcome login----")
                func(*arg, **kwargs)
          else:
              print("----wrong password----")
      return wrapper
    
    @auth2
    def task2(name, is_happy="yes"):
                    print("{0} is doing somthing, is he happy? [{1}]".format(name, is_happy))
    
    
    if __name__ =="__main__":
         '''带参数的装饰器'''
         task2("faily")

    可以看出,被装饰的函数task2 拥有两个参数,name, is_happy, 那么如何将两个参数传递进装饰器函数中呢?

  1. 根据前面我们所学的知识,可以看出, 装饰器在执行的时候, 首先将被装饰的 函数名 这里是task2传递给装饰器函数 auth2, 那么被装饰函数的参数 就不得不找别的地方传进去, 唯一可以接受这些变量的地方就是 wrapper函数.
  2. 通过wrapper 函数将参数传进去之后呢, 再建参数传递个 被装饰的函数 即可

被装饰的函数有返回值

  • 显然 这种函数最普遍, 函数在执行后立即展示结果,而是作为值进行传递

    from functools import wraps
    
    def auth3(func):
    
      @wraps(func)
        def wrapper(arg):
            user = input("pls input password:").strip()
            if user == "faily":
                print("---welcome login----")
                return func(arg)
            else:
                print("----wrong password---")
    
        return wrapper
    
    @auth3
    def task3(name):
        print("do somthing ")
        return name
    
    if __name__ == "__main__":
        ts = task3("momo")
        print(ts)

    这种很好理解, 函数有返回值, 那么在wrapper函数内执行完新加功能后, 直接return 被装饰的函数即可

在函数中嵌入装饰器

在这里我们可以这样理解: 既然装饰器是高阶函数 + 嵌套函数 = 装饰器 那么可不可 有这种组合呢,即 装饰器 + 嵌套函数的形式呢?

答案是肯定的

其实这种应用也可理解为. 之前我们写的所有装饰器都是自己不带参数的, 有没有想过 带有参数的 装饰器该如何实现呢?

  • 回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件

    from functools import wraps
    
    def logit(logfile='out.log'):
    
        # 装饰器 
        def logging_decorator(func):
            @wraps(func)
            def wrapped_function(*args, **kwargs):
                log_string = func.__name__ + " was called"
                print(log_string)
                # 打开logfile,并写入内容
                with open(logfile, 'a') as f:
                    # 现在将日志打到指定的logfile
                    f.write(log_string + '\n')
                return func(*args, **kwargs)
            return wrapped_function
        return logging_decorator
    
    #默认装饰器参数
    @logit()
    def myfunc1(action="run"):
        if action == "run":
            return true
        else:
            return false
    
    #装饰器传参
    @logit(logfile='func2.log')
    def myfunc2(action = "stop"):
        if action == "run":
            return true
        else:
            return false
    
    if __name__ == "__main__":
      myfunc1()
      myfunc2()
    
    # 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串 

    这个例子中, 是不是决定装饰器实在是太强大了, 我们可以通过为装饰器给予不同的参数实现不同函数日志输出的新增功能, 我们只是在标准装饰器外外加了一层函数,这个函数实际就是为了用来将装饰器的参数传递进包裹函数中.

装饰器类

场景分析 : 在运维监控中, 我们常常需要记录不同级别,或者不同app产生的日志,但当我们的应用程序的某些部分还比较脆弱时,触发异常也许是需要更加关注的事情. 比方说有时只想记录日志到一个文件; 而有时你想在程序发生异常时送到一个email,同时也保留日志。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。

幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit

  • 装饰器类

    rom functools import wraps
    
    class logit(object):
        '''装饰器类'''
        def __init__(self, logfile='out.log'):
            self.logfile = logfile
    
        def __call__(self, func): # __call__说明这是一个callable
            @wraps(func)
            def wrapped_function(*args, **kwargs):
                log_string = func.__name__ + " was called"
                print(log_string)
                # 打开logfile并写入
                with open(self.logfile, 'a') as f:
                    # 现在将日志打到指定的文件
                    f.write(log_string + '\n')
                # 现在,发送一个通知
                self.notify()
                return func(*args, **kwargs)
            return wrapped_function
    
        def notify(self):
            # logit只打日志,不做别的
            pass

    这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

    @logit()
    def myfunc1():
        pass

    现在,我们给logit创建子类,来添加email的功能。

    class email_logit(logit):
        '''
        一个logit的实现版本,可以在函数调用时发送email给管理员
        '''
        def __init__(self, email='admin@myproject.com', *args, **kwargs):
            self.email = email
            super(email_logit, self).__init__(*args, **kwargs) # 集成父类
    
        def notify(self):
            # 发送一封email到self.email
            # 这里就不做实现了
            pass

    从现在起,@email_logit将会和@logit产生同样的效果,但是在输出日志的基础上,还会多发送一封邮件给管理员。

总结

装饰器进阶已经讲完, 下一篇我们继续研究, 多重装饰器