zoukankan      html  css  js  c++  java
  • day4装饰器

    Python装饰器

    1.必备

      def foo():

        print(foo)

      <function foo at 0x7f62db093f28>
      >>> foo
      <function foo at 0x7f62db093f28>

      foo是函数,函数体;

      foo()是执行foo函数

        def foo():

        print(foo)

      foo = lambda x:x+1

      foo()将执行lambda表达式,而不再是原来的foo函数,这是因为函数foo被重新定义了。

    2.需求来了

        初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

      ##############基础平台提供的功能如下###############
        def f1():

        print(f1)

      def f2():

        print(f2)

        def f3():

        print(f3)

      def f4():

        print(f4)

      ############### 业务部门A 调用基础平台提供的功能 ###############

        f1()

      f2()

      f3()

      f4()

      ############### 业务部门B 调用基础平台提供的功能 ###############

        f1()

      f2()

      f3()

      f4()

      目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码的时候没有关注验证相关的问题,即:基础平台提供的功能可以被任何人使用。现在需要对基础平台的功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

      老大把工作交给了Low B,他是这么做的:

      跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台功能之前先验证,艾,这样一来,基础平台就不需要进行任何修改了。

      不幸的是,当天Low B就被开除了....

        老大把工作交给了Low BB,他是这么做的:

      只对基础平台的代码进行重构,让N个业务部门无需做任何修改

      ##############基础平台提供的功能如下###############

        def f1():

        #验证1

        #验证2

        #验证3

        print(f1)

      def f2():

        #验证1

        #验证2

        #验证3

        print(f2)

      def f3():

        #验证1

        #验证2

        #验证3

        print(f3)

      def f4():

        #验证1

        #验证2

        #验证3

        print(f4)

      ############### 业务部门A 调用基础平台提供的功能 ###############

      f1()

      f2()

      f3()

      f4()

      ############### 业务部门B 调用基础平台提供的功能 ###############  

        f1()

      f2()

      f3()

      f4()

      ############### 业务部门调用方法不变,不影响业务部门 ###############  

      过了一周Low BB也被开除了......

      上述代码的重用性太差,而且修改了原代码。

      老大把工作交给了Low BBB,他是这么做的:

      只对基础平台的代码进行重构,其他业务部门无需做任何修改。

      def check_login():

        #验证1

        #验证2

        #验证3

        pass

      def f1():

        check_login()

        print(f1)

      def f2():

        check_login()

        print(f2)

      def f3():

        check_login()

        print(f3)

      def f4():

        check_login()

        print(f4)

      老大看了一下Low BBB的实现,嘴角露出了一丝欣慰的笑,语重心长的跟Low BBB聊了个天:

      老大说:

      写代码要遵循开放封闭的原则,虽然在这个原则是用在面向对象开发,但是也是用与函数式编程,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

      (1)封闭:以实现的功能代码块

      (2)开放:对扩展开发,可以开发新功能,但尽量不要修改原代码。

      如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3、f4的内部修改代码,老大就被给了Low BBB一个实现方案:

      def w1(func):

        def inner():

          #验证1

          #验证2

          验证3

          return func()

      return inner

      @w1

      def f1():

        print(f1)

      @w1

      def f2():

        print(f2)

      @w1

      def f3():

        print(f3)

      @w1

      def f4():

        print(f4)

      ############### 业务部门调用方法不变,不影响业务部门 ###############

        对于上述代码,也是仅仅对基础平台的代码进行了修改,就可以实现在其他人调用函数f1,f2,f3,f4之前都进行[验证]的操作,并且其他部门无需做任何修改。

      Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?

      老大正要生气,突然Low BBB的手机掉到了地上,恰巧屏保就是Low BBB的女朋友照片,老大一看一紧一抖,嘻笑眼开,交定了Low BBB这个朋友。详细的讲解开始了:

    单独以f1为例:

      def w1(func):

        def inner():

          #验证1

          #验证2

          #验证3

          return func()

      return inner

      @w1

      def f1():

        print(f1)

      当写完这段代码后(函数未被执行、未被执行、未被执行),Python解释器就会从上到下解释代码,步骤如下:

      (1)def w1(func):   ==>将w1加载到内存,扫描加载到内存。

      (2)@w1

        没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

      从表面上看解释器着实会执行这两句,但是@w1这一句代码却大有文章,@函数名是Python的一种语法糖(装饰器)。

    如上例@w1内部会执行以下操作:

      (1)执行w1函数,并将@wq下面的函数作为w1函数的参数,即:@w1等价于w1(f1)

      所以,内部就会去执行:

        def inner():

          #验证

          return f1()  #func是参数,此时func等价于f1

        return inner     #返回的inner,inner代表的是函数,非执行函数

      其实就是将原来的f1函数塞进另外以一个函数中

      (2)将执行玩的w1函数返回值赋值给@w1下面的函数的函数名

      w1函数的返回值是:

        def inner():

           #验证

          return f1()  #此处表示原来的f1函数

      然后,将此返回值在重新赋值给f1,即:

      新f1 = def inner:

          #验证

        return f1()

      所以,以后业务部门想要执行f1函数时,就会执行新f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1函数的返回值返回给了业务调用者。

        Low BBB你明白了吗?要是没有明白的话,我晚上去你家帮你解决吧!!!

    3.问答时间

    问题:被装饰的函数有参数呢?

      一个参数:

      def w1(func):

        def inner(arg):

          #验证1

          #验证2

          #验证3

          return func(arg)

      return inner

      @w1

      def f1(arg):

        print(f1)

      二个参数:

      def w1(func):

        def inner(arg1,arg2)

          #验证1

          #验证2

          #验证3

          return func(arg1,arg2)

        return inner

      @w1

      def f1(arg1,arg2):

        print(f1)

      三个参数:

      def w1(func):

        def inner(arg1,arg2,arg3):

          #验证1

          #验证2

          #验证3

          return func(arg1,arg2,arg3)

      return inner

      @w1

      def f1(arg1,arg2,arg3):

        print(f1)

    问题:可以装饰具有处理n个参数的函数的装饰器?

      def w1(func):

        def inner(*args,**kwargs):

          #验证1

          #验证2

          #验证3

          return func(*args,**kwargs)

      return inner

      @w1

      def f1(arg1,arg2,arg3):

        print(f1)

    问题:一个函数可以被多个装饰器装饰码?

        def w1(func):

        def inner(*args,**kwargs):

          #验证1

          #验证2

          #验证3

          return func(*args,**kwargs)

      return inner  

      def w2(func):

        def inner(*args,**kwargs):

          #验证1

          #验证2

          #验证3

        return func(*args,**kwargs)

      return inner

      @w1

      @w2

      def f1(arg1,arg2,arg3):

        print(f1)

    下面有一个例子,需要我们来完成,我们需要设置一个网页后台,当用户登录非主页的时候需要验证,如何实现呢?

      首先,我们先定义好各个模块:

      def login(func):
      #登录验证模块
        print("passed user verification......")
        return func

      def home(name):

      #主页,不需要登录验证
        print("Welcome [%s] to home page." %name)
      def tv(name):

        #TV页面,进来需要验证登录
        print("Welcome [%s] to TV page." %name)
      def movie(name):

        #movie页面,进来需要登录验证
        print("Welcome [%s] to movie page." %name)

      上面代码,我们实现了几个函数体,然后,我们知道,要想让用户进行验证,那么在进入网页的时候要先执行login函数,如何执行函数呢,必须调用函数,调用函数之后有一个返回值,我们知道,要不修改原代码以及方便用户操作,用户只需要输入tv(name)即可,这样就直接调用的是tv模块,因为。我们知道,程序是按照串行的方式执行的。那么,我们可以事先执行login()模块,然后返回一个值。在执行tv函数。

      def login(func):
      #登录验证模块
        print("passed user verification......")
        return func

      def home(name):
        print("Welcome [%s] to home page." %name)
      def tv(name):
        print("Welcome [%s] to TV page." %name)
      def movie(name):
        print("Welcome [%s] to movie page." %name)
      tv = login(tv)
      #tv当作参数传递给login函数,然后返回tv参数,目的就是为了执行一次验证
      tv("alex")

      运行结果如下:

        passed user verification......
      Welcome [alex] to TV page.
      虽然我们完美执行了这个程序。但是这个程序有问题,因为用户调用的时候输入的是tv("alex"),那么在没有调用的时候运行程序会是怎样的:

        def login(func):
        #登录验证模块
        print("passed user verification......")
        return func

      def home(name):
        print("Welcome [%s] to home page." %name)
      def tv(name):
        print("Welcome [%s] to TV page." %name)
      def movie(name):
        print("Welcome [%s] to movie page." %name)

      tv = login(tv)
      #tv当作参数传递给login函数,然后返回tv参数
      #tv("alex")

      运行结果如下:

      passed user verification......

      此时,我们并没有调用主页,但是也提示让用户进行输入验证,这样就没有太多实际意义了,因此要想办法让用户没有调用的时候什么都不打印。

        def login(func):
        #登录验证模块
        print("passed user verification......")
        return func

      def home(name):
        print("Welcome [%s] to home page." %name)
      @login
      def tv(name):
        print("Welcome [%s] to TV page." %name)
      def movie(name):
        print("Welcome [%s] to movie page." %name)

      #tv = login(tv)
      #tv当作参数传递给login函数,然后返回tv参数
      #tv("alex")

      #login相当于tv = login(tv)当程序没有调用的时候执行也返回让用户调用,显然是不合理的。这样没有执行调用就需要验证显然是不合理的,要想办法让程序没有调用的时候不要指定调用。

      def login(func):
      #登录验证模块
        def inner(name):
        #设置让用户调用的时候执行,否则不执行,避免没有调用就执行。
          print("passed user verification......")
          func(name)
        return inner

      def home(name):
        print("Welcome [%s] to home page." %name)
      def tv(name):
        print("Welcome [%s] to TV page." %name)
      def movie(name):
        print("Welcome [%s] to movie page." %name)

      tv = login(tv)
      #tv当作参数传递给login函数,然后返回tv参数
      #tv("alex")

      在上面程序中,我们在验证模块嵌套了一层函数,用于让用户在没有调用的情况下,执行程序不会执行验证模块,为什么可以呢?我们知道,执行函数需要函数名function()加上括号才能够执行,因此我们在这行循环的时候返回内存函数名,然后进行调用,这样就能够在用户验证的时候执行验证模块,而在用户没有调用的过程中,返回函数名,但是这样时候因为只有函数名,因而是不能够执行函数的。

      下面来看看用装饰器实现的情况:

      def login(func):
        #登录验证模块
        def inner(name):
          #设置让用户调用的时候执行,否则不执行,避免没有调用就执行。
          print("passed user verification......")
          func(name)
        return inner

      def home(name):
        print("Welcome [%s] to home page." %name)
      @login
      def tv(name):
        print("Welcome [%s] to TV page." %name)
      @login
        def movie(name):
      print("Welcome [%s] to movie page." %name)

      #tv = login(tv)
      #tv当作参数传递给login函数,然后返回tv参数
      tv("alex")
      movie("tom")
      

      上面代码中,我们使用了装饰器,让程序在运行的过程中首先使用了装饰器,装饰器相当于tv = login(tv)让第一次函数返回的值赋值给tv,这样在用户调用tv函数的时候,其实并没有执行tv函数的代码,而是执行内存函数的代码,就直接调用第二次验证模块,验证完成之后,我们指定用户打印模块。这样我们就避免了在用户还没有调用的时候就执行了验证模块。  

  • 相关阅读:
    2014年第五届蓝桥杯省赛试题(JavaA组)
    2013年第四届蓝桥杯省赛试题(JavaA组)
    2013蓝桥杯JavaA组T10 大臣的旅费(树的直径)
    CodeForces
    天梯赛 L2-006 树的遍历(序列建树)
    PAT甲 1095 解码PAT准考证/1153 Decode Registration Card of PAT(优化技巧)
    2015年第六届蓝桥杯省赛T10 生命之树(树形dp+Java模拟vector)
    ZOJ
    SPOJ
    HDU
  • 原文地址:https://www.cnblogs.com/gengcx/p/6790039.html
Copyright © 2011-2022 走看看