装饰器

一、概念

装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。比如我们要给每一个函数新增一个打印logger日志,我们就可以使用装饰器,在不修改原函数的前提下,新增功能又能做到代码的解耦

二、装饰器的几个原则及学习装饰器必要的知识储备

  • 1、装饰器的几个原则

    • 1.不能修改被装饰的函数
    • 2.不能修改被装饰的函数的调用方式
  • 2、学习装饰器必要的知识

    • 1.函数的理解
    • 2.高阶函数
    • 3.函数的嵌套

三、装饰的原理

  • 1、定义一个装饰器(计算函数执行时间)

    import time
    def timer(func):
      def wrapper():
          start_time = time.time()
          res = func()
          end_time = time.time()
          print('程序运行时间:{0}'.format(end_time - start_time))
          return res
      return wrapper
    
  • 2、定义一个要执行的函数

    def foo():
        time.sleep(3)
        print('主函数')
    
  • 3、根据上面几个原则,我们来调用函数foo

    if __name__ == "__main__":
        foo = timer(foo)
        foo()
    
  • 4、解答上面的代码

    • 1.定义的装饰器是一个高阶函数,被装饰的函数作为参数传递进去
    • 2.在满足装饰的原则下,我们把高阶函数执行后重新赋值给foo函数
    • 3.foo()函数的执行就没改变原有函数的调用方式
  • 5、使用装饰器@

    上面第四点中2和3步骤在python中直接使用@语法糖来处理

    @timer
    def bar():
        time.sleep(2)
        print('主函数')
    
    if __name__ == "__main__":
        bar()
    

四、被装饰的函数带参数

不太懂函数的参数、可变参数、关键字参数的可以回顾下函数的认识这一章节

  import time

  def timer(func):
      def wrapper(*args, **kwargs):
          start_time = time.time()
          res = func(*args, **kwargs)
          end_time = time.time()
          print('执行时间:{0}'.format(end_time - start_time))
          return res

      return wrapper

  @timer
  def foo(name, gender):
      time.sleep(3)
      print(name)
      print(gender)

  if __name__ == "__main__":
      foo('张三', gender='男')

五、模拟用户登录的装饰器

如果你使用过django就会使用到一个用户登录拦截的装饰器

  def auth_login(func):
    def wrapper(*args, **kwargs):
        username = input('用户名:').strip()
        passwd = input('密码:').strip()
        if username == 'hello' and passwd == 'word':
            res = func(*args, **kwargs)
            return res
        else:
            print('你输入的用户名或者密码错误')

    return wrapper

  @auth_login
  def foo():
      print('主页')

  if __name__ == "__main__":
      foo()

六、装饰器传递参数

  • 我们学习函数的时候就说过foo()带了()就表示函数执行,不带()仅仅是返回一个函数的内存地址
  • 如果装饰器要传递参数进去,我们自然想的是多嵌套一层函数
  # 定义一个用户登录的装饰,默认是手机号码登录(也有可能是email,username方式登录)
  def auth(type='mobile'):
      def auth_func(func):
          def wrapper(*args, **kwargs):
              if type == 'mobile':
                  print('你是用手机号码登录')
                  print(args, kwargs)
                  res = func(*args, **kwargs)
                  return res
              elif type == 'email':
                  print('email方式登录')
                  res = func(*args, **kwargs)
                  return res
              elif type == 'username':
                  print('username方式登录')
                  res = func(*args, **kwargs)
                  return res

          return wrapper

      return auth_func

  @auth('mobile')
  def test(mobile, passwd):
      print('主页')

  @auth('email')
  def test1():
      print('主页1')

  if __name__ == "__main__":
      test('110', '123')
      test1()

七、使用类来创建一个装饰器

创建一个数据库操的log的装饰器

  • 1、具体实现代码

    from functools import wraps
    from datetime import datetime
    
    # 创建一个类的装饰器
    class Log(object):
        def __init__(self, logfile='log.log'):
            self.logfile = logfile
    
        def __call__(self, func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                self.writelog(*args, **kwargs)
                return func(*args, **kwargs)
    
            return wrapper
    
        # 把日志写到本地
        def writelog(self, *args, **kwargs):
            time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            log_str = time + ' 操作人:{0[0]} 进行了【{0[1]}】操作'.format(args)
            # 写入本地文件中
            with open(self.logfile, 'a', encoding='utf8') as file:
                file.write(log_str + '\n')
    
    @Log()
    def printLog(name, type):
        print('姓名:{0},type:{1}'.format(name, type))
    
    if __name__ == "__main__":
        printLog('张三', '查询')
        printLog('李四', '新增')
    
  • 2、执行结果(本地文件夹下多一个文件)

    2018-06-24 10:47:40 操作人:张三 进行了【查询】操作
    2018-06-24 10:47:40 操作人:李四 进行了【新增】操作
    

results matching ""

    No results matching ""