python基础学习之描述符和装饰器

  • 描述符的了解:python

    描述符协议:闭包

    python描述符是一个“绑定行为”的对象属性,在描述符协议中,它能够经过方法重写属性的访问。这些方法有:函数

         __get__, __set__, 和__delete__测试

    若是这些方法中的任何一个被定义在一个对对象

    象中,这个对象就是一个描述符
  • 总结下说人话:就是当一个类中实例化另外一个类的时候,经过本类来调用另外一个类的功能,控制实例对象 访问 这个属性作一些额外的操做递归

    class Fee:       #先定义一个Fee类
        def __get__(self, instance, owner):
            print('get:疏楼龙宿人间至帅')
        def __set__(self, instance, value):
            print('set:疏楼龙宿%s'%value)
        def __delete__(self, instance):
            print('delete:疏楼龙宿人间无敌')get

    class Fdd:      #在定义一个类Fdd
        fee = Fee()    #咱们在Fdd中实例化Feeclass

  • 咱们能够经过Fdd实例化来调用Fee中功能,如何实现呢?test

  • a = Fdd()     #先实例化,接下来咱们须要了解功能的对应调用状况import

    a.fee       # 即调用fee属性,fee属性是什么,就是Fee实例化,这时候会触发__get__功能

    a.fee = '人间至帅'             #即对fee属性进行修改,会触发类Fee中的__set__功能,将修改内容传入参数value

    del a.fee         # 删除a实例化中fee属性,就是要删掉Fee(),会触发Fee类中__delete__属性

装饰器:重点啊重点啊重点啊!

    • 什么是装饰器?

      装饰器本质上也是函数,他是用来修饰其余函数,给其余函数添加额外功能的

    • 装饰器原则:

      1--不改变被修饰函数的源代码

      2--不改变被修饰函数的调用方式

    • 装饰器的本质是什么?

      高阶函数+函数嵌套+闭包

    • 咱们回顾下,什么是高阶函数?

      --函数接受的参数是另外一个函数的函数名

      --函数的返回值另外一个函数的函数名

      知足上述两个条件中的一个,能够称为高阶函数

      【注意:若是返回值是这个函数自己的调用方式,那么这是递归!】

    • 咱们再回顾下,什么闭包?

      就是函数的返回值是其内部嵌套函数的函数名

    • 按照上述的规则,咱们来推导下装饰器应该是什么样子

    • 好比,咱们须要测试一下isinstance()这个函数的运行时间

      首先,根据原则要求,咱们不能改变被修饰函数的源代码,也就是说,isinstance()函数写完后要放在一边,咱们先写一个运行代码:

    • def test_isinstance():
          for i in range(0,100000):
              if isinstance(i,int) is True:
                  pass
          pass

      因为一次运行的时间过短,咱们运行10W次,

      按照原则1,这个时候,这个代码就不能动了,咱们应该如何测试他的时间呢?

    • 导入时间模块包,写一段高阶函数,将上述函数传入其中做为参数,在运行前,记录下时间,而后运行该函数,运行完,再记录下时间,作个差运算,得出时间差,即运行时间

      import time
      def test_time(fun):
              star_time = time.time()
              fun()
              stop_time = time.time()
              run_time = stop_time - star_time
          print('运行时间是%s' %run_time)

    • OK,咱们已经完美的得出了运行时间,并且源代码也没动

    • 那么问题来,函数调用方式,按照test_isinstance()的方式调用,咱们没有获得运行时间。。。。。若是要同时获得结果,咱们就要使用test_time(test_isinstance)这个方法,和原则2冲突

    • 到目前为止,咱们已经解决了需求问题,即测试代码运行时间,那么若是进一步使调用方式不变呢?咱们想到了高阶函数中的返回函数名,若是我返回值是须要测试的函数的函数名呢?

    • 代码:

      def test_isinstance():
          for i in range(0,100000):
              if isinstance(i,int) is True:
                  pass
          pass

      import time
      def test_time(fun):
          star_time = time.time()
          fun()
          stop_time = time.time()
          run_time = stop_time - star_time
          print('运行时间是%s' %run_time)

          return test_isinstance

    • 这个时候,咱们再作以下操做:

      test_isinstance = test_time(test_isinstance)

      问题彷佛解决了,我直接使用就能够获得结果

    • 至此,咱们已经获得了 装饰器的雏形,至于最后一部复制问题,装饰器的格式,能够解决,又称为 语法糖

      @test_time

      def test_isinstance():

          .......

          .......

      最开的时候@test_time的功能效果便是:

      test_isinstance = test_time(test_isinstance)

    • 仔细检查后,发现咱们彷佛运行了两次test_isinstance函数?

      test_time中运行了一次,接受返回函数名后咱们加上括号,又运行了一次,问题来了,这下怎么办?

    • 遇到问题,解决问题,多运行了一次,咱们看看能不能少运行一次

      常规手段没法达成目的,咱们回忆下装饰器的的本质是什么

      高阶函数(已有)+函数嵌套(无)+闭包(无)

      咱们还有其余手段能够用,好比函数嵌套,和闭包

    • 咱们要实现的终极目的是test_isinstance()的同时会让上述的两个函数都运行一次,且只运行一次,并且,要得到原函数结果,上述test_time函数已经能够得到时间,同时喊运行了一次test_isinstance函数,若是咱们截取下他内部test_isinstance的运行结果,做为本函数的返回值呢?

      import time

      def test_time(fun):
          star_time = time.time()
          res = fun()
          stop_time = time.time()
          run_time = stop_time - star_time
          print('运行时间是%s' %run_time)

          return res

      咱们能够发现,运行这个函数,咱们能够获得和运行test_isinstance函数同样的结果,由于返回值就是他的运行结果

    • 咱们彷佛离真相愈来愈近了

    • 如同上面说的同样,咱们须要test_isinstance()的时候,让函数运行一次,而不是两次,函数运行是加括号直接运行,少运行一次,咱们就要少一个括号,即test_isintance的时候不运行,test_isintance上面咱们等于了 test_time(test_istntance),也便是,这个时候,我不运行函数,等加上括号的时候,在运行函数,兼顾上述咱们测试出来所须要的全部功能【增长附加功能,获取本函数原返回值】

    • 通过思考和测试,使用闭包功能

          import time
          def test_time(fun):
              def test():
                          star_time = time.time()
                          res = fun()
                          stop_time = time.time()
                          run_time = stop_time - star_time
                  print('运行时间是%s' %run_time)
                  return res
              return test

    • 解释代码:

      在测试函数上挂上@test_time,即:test_isintance = test_time(test_isintance),这个时候,同等于test_isintance=test【由于装饰器函数test_time的返回值是test,test为内嵌函数】;当test_isintance加上括号()运行的时候,等于运行了test()而后会进行下面一系列操做,同时,会返回原函数的返回值。

    • 至此,装饰器函数出炉了。

    • 增长问题:

      若是我函数自己就有参数呢。。。。。。。

      好比:test_isintance(1,2,3,4,5)

      这个等于什么?不就等于 test(1,2,3,4,5) 么!

      也就是说我内嵌函数中,有可能会有参数!代码,须要普适性,这种状况也须要考虑进去!参数形式能肯定么?彷佛不能,一样,咱们也不该该根据测试函数的参数特色再去修改函数。

    • 咱们须要把参数传给,那么函数参数形参和实参的赋值关系主要分为哪几类?

      位置参数、关键字参数,即*args **kwargs,咱们在test_isintance的括号内加上参数,也就是在test的括号内加上参数,多是0个,多是若干个,*args和**kwargs完美接收了上述参数,在本来的函数test_isintance()的参数传给test()后,再由test在代码块内部传给test_isintance()【即代码中的fun()】

    • 最后咱们的代码应该以下:

    • import time
      def test_time(fun):
          def test(*args,**kwargs):
                      star_time = time.time()
                      res = fun(*args,**kwargs)
                      stop_time = time.time()
                      run_time = stop_time - star_time
              print('运行时间是%s' %run_time)
              return res
          return test

    • 至此,装饰器彻底体

    • 使用方式:在上述代码已经提早写好的状况下,使用语法糖便可

    • @test_time

      def test_isinstance():    for i in range(0,100000):        if isinstance(i,int) is True:            pass    pass

相关文章
相关标签/搜索