装饰器

python学习网 2019-05-17 22:43:03

装饰器

  1. 开放封闭原则:

    开放封闭原则
  • 开放:对代码的拓展开放的,允许代码扩展、添加新功能。如更新地图,加新枪,等等。
  • 封闭:对源码的修改是封闭的。

    装饰器

  • 装饰器:完全遵循开放封闭原则。
  • 装饰器: 在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。
  • 装饰器就是一个函数。装饰器的本质是闭包。

  1. 装饰器的初识:

    • 版本一: 写一些代码测试一下index函数的执行效率。
    import time
     def index():
         '''有很多代码.....'''
         time.sleep(2) # 模拟的网络延迟或者代码效率
         print('欢迎登录博客园首页')
    
     def dariy():
         '''有很多代码.....'''
         time.sleep(3) # 模拟的网络延迟或者代码效率
         print('欢迎登录日记页面')
    
    # print(time.time())  # 格林威治时间。
    # 版本一有问题: 如果测试别人的代码,必须重新复值粘贴。
    start_time = time.time()
    index()
    end_time = time.time()
    print(end_time-start_time)
    #结果:
    欢迎登录博客园首页
    2.0004754066467285
    
    start_time = time.time()
    dariy()
    end_time = time.time()
    print(end_time-start_time)
    #结果:
    欢迎登录日记页面
    3.0002753734588623
    
    • 版本二:利用函数,解决代码重复使用的问题

      import time
      def index():
          '''有很多代码.....'''
          time.sleep(2) # 模拟的网络延迟或者代码效率
          print('欢迎登录博客园首页')
      
      def timmer(f):  # f= index 传参
          start_time = time.time()
          f()  # index()
          end_time = time.time()
          print(f'测试本函数的执行效率{end_time-start_time}')
      
      timmer(index)  #index()--->原函数的调用方式
      
      版本二还是有问题: 原来index函数源码没有变化,并给原函数添加了一个新的功能:测试原函数的执行效率。
      满足开放封闭原则么?不满足,因为原函数的调用方式改变了。
      
    • 版本三:不能改变原函数的调用方式。

       import time
       def index():
           '''有很多代码.....'''
           time.sleep(2) # 模拟的网络延迟或者代码效率
           print('欢迎登录博客园首页')
      
      #timmer()是一个装饰器,本质是:  闭包
      def timmer(f):  # f = index  (funciton index123)内存地址
           def inner():  # inner :(funciton inner123)
               start_time = time.time()
               f()  # index() (funciton index123)
               end_time = time.time()
               print(f'测试本函数的执行效率{end_time-start_time}')
           return inner  # (funciton inner123)
      index = timmer(index)  # inner (funciton inner123)
      index()  # inner()
      
      
      def func():
         print('in func')
      
      func() #函数名func指向内存地址
      func = 666  #func指向666
      func(0)#报错
      
    • 版本四:具体研究

    • 版本五:python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器

      import time
      # timmer装饰器 --->装饰器放在所有函数最上面
      def timmer(f):
          def inner():
              start_time = time.time()
              f()
              end_time = time.time()
              print(f'测试本函数的执行效率{end_time-start_time}')
          return inner
      
      @timmer #  @timmer读两行,相当于index = timmer(index) 
      def index():
          '''有很多代码.....'''
          time.sleep(0.6) # 模拟的网络延迟或者代码效率
          print('欢迎登录博客园首页')
      index()
      #结果:
      欢迎登录博客园首页
      测试本函数的执行效率0.6001222133636475
      
      
    • 版本六:被装饰的函数带返回值

      import time
      # timmer装饰器
      def timmer(f): # f = index
          def inner():
              start_time = time.time() 
              r = f()  # index()
              end_time = time.time()
              print(f'测试本函数的执行效率{end_time-start_time}')
              return r  #加上此句
          return inner
      
      @timmer # index = timmer(index)
      def index():
          '''有很多代码.....'''
          time.sleep(0.6) # 模拟的网络延迟或者代码效率
          print('欢迎登录博客园首页')
          return 666
      ret = index()  # inner()
      print(ret)
      #结果:
      欢迎登录博客园首页
      测试本函数的执行效率0.6007750034332275
      666
      # 加上装饰器不应该改变原函数的返回值,所以666 应该返回给我下面的ret,但是下面的这个ret实际接收的是inner函数的返回值,而666真正返回给的是装饰器里面的f() 也就是 r,我们现在要解决的问题就是将r给inner的返回值,所以变成 inner函数的返回值为 return r。
      
    • 版本七:被装饰的函数带参数

      import time
      # timmer装饰器
      def timmer(f):   # f = index
          def inner(*args,**kwargs):
              #  函数的定义:* 聚合  args = ('李舒淇',18)
              start_time = time.time()
              r = f(*args,**kwargs) # 给def index(name,age)传参
              # 函数的执行:* 打散:f(*args) --> f(*('李舒淇',18))  --> f('李舒淇',18)
              end_time = time.time()
              print(f'测试本函数的执行效率{end_time-start_time}')
              return r
          return inner
      
      @timmer # index = timmer(index)
      def index(name,age):   #
          '''有很多代码.....'''
          time.sleep(0.6) # 模拟的网络延迟或者代码效率
          print(f'欢迎{age}岁{name}登录博客园首页')
          return 666
      index('李舒淇',18)  # inner('李舒淇',18)
      #结果:
      欢迎18岁李舒淇登录博客园首页
      测试本函数的执行效率0.6005463600158691
      标准版的装饰器;
      
      def wrapper(f):
          def inner(*args,**kwargs):
              '''添加额外的功能:执行被装饰函数之前的操作'''
              ret = f(*args,**kwargs)
              ''''添加额外的功能:执行被装饰函数之后的操作'''
              return ret
          return inner
      

  2. 装饰器的应用

    # 装饰器的应用:登录认证。模拟博客园登录的作业。装饰器的认证功能。
    #程序框架:
    
    
    def login():
        pass
    
    
    def register():
        pass
    
    
    status_dict = {
        'username': None,
        'status': False,
    }
    
    def auth(f):
        '''
        你的装饰器完成:访问被装饰函数之前,写一个三次登录认证的功能。
        登录成功:让其访问被装饰得函数,登录没有成功,不让访问。
        :param f:
        :return:
        '''
        def inner(*args,**kwargs):
            '''访问函数之前的操作,功能'''
            if status_dict['status']:  #即status_dict['status']对应的值为True
                ret = f(*args,**kwargs)
                '''访问函数之后的操作,功能'''
                return ret
            else:
                username = input('请输入用户名').strip()
                password = input('请输入密码').strip()
                if username == 'taibai' and password == '123':
                    print('登录成功')
                    status_dict['username'] = username
                    status_dict['status'] = True
                    ret = f(*args, **kwargs)
                    return ret
                else:
                    print('登录失败')
        return inner
    @auth  # article = auth(article)
    def article():
        print('欢迎访问文章页面')
    @auth
    def comment():
        print('欢迎访问评论页面')
    @auth
    def dariy():
        print('欢迎访问日记页面')
    
    article()  # inner()
    comment()  #inner()
    dariy()   #inner
    
    #结果:
    请输入用户名taibai
    请输入密码123
    登录成功
    欢迎访问文章页面
    欢迎访问评论页面
    欢迎访问日记页面
阅读(3633) 评论(0)