装饰器

装饰器的开放封闭原则

  1.封闭

    ①不能修改被装饰对象的源代码

    ②不能更改被装饰对象的调用方式

  2.开放

    对扩展开放,在封闭的条件下,添加新功能

装饰器

  def  fn():

    print('原有功能')

  #装饰器

  def  outer(tag):

    def inner():

      print('原函数前新增功能')

      tag()

      print('原函数后新增功能')

    return inner

  fn=outer(fn)

  fn()

  上面就是简单的装饰器,其实本质上,就是一种闭包的运用,将原fn的函数地址存放在tag中,新fn的函数地址就是outer(fn)的返回值,也就是inner函数的地址,所以在之后运行fn其实是在运行inner函数。

语法糖

  def outer1(f):

    def inner():

      f()

      print('新功能1')

    return inner 

 

  def outer2(f):

    def inner():

      f()

      print('新功能2')

    return inner

  

  @outer1  #  等同于  fn=outer(outer2(fn))

  @outer2     #  等同于  fn=outer2(fn)  

  def fn():

    print('原功能')

  上面的@(外层函数)的方法叫做语法糖,语法糖叠加时运行方式自上而下,加载顺序自下而上。

装饰有参数的函数

  上述的代码中,fn()都没有带参数,其实加参数一样,逆推一下,如果fn需要加参数,新的fn地址指向inner的函数地址,而调用方式不能改变,所以inner函数要添加与fn相同的参数,tag是原fn的内存地址,所以自然也要添加。从这里,可以得出结论,inner,fn(新),tag(原fn)三者在调用时得到的参数必须相同(因为封闭开放原则,除了添加功能其他都要相同),同理如果原函数有返回值,那么这三者都必须有返回值。(个人感觉,fn和inner就像是参数的中转站,目的是为了把参数赋给tag(原函数)去运行)所以在第一个代码中需要添加参数就是:

  def  fn(a,b):

    print('原有功能')

  def  outer(tag):

    def inner(a,b):

      print('原函数前新增功能')

      tag(a,b)

      print('原函数后新增功能')

    return inner

  fn=outer(fn)

  fn()

 可变长参数的装饰器

  其实可变长的参数,是为了解决多个函数需要添加同一个装饰,而函数中的形参多样的(一个或者多个又或者有关键词参数)的问题,这里无非就是运用了之前的打包与解包(打散)的方法。如果把上面代码的形参换成*args和**kwargs(之前的可变长参数中有提及)那么无论原函数是否有参数或者有多少参数都可以打包给arg或者kwargs,然后再解包赋给所带该参数的函数。打个比方,虽然原函数fn(a,b)和新函数fn(a,b)(封闭开放原则)的形参都是a,b,而inner,tag(原fn函数地址)的形参都是*args,**kwargs,tag运行时本质上得到的都是打包传递过来后又解包的a,b所带的参数值。

  def  fn(a,b):

    print('原有功能')

  def  outer(tag):

    def inner(*args,**kwargs):

      print('原函数前新增功能')

      tag(*args,**kwargs)

      print('原函数后新增功能')

    return inner

  fn=outer(fn)

  fn(a,b)

带参装饰器

   如果我们在添加装饰器的时候,除了需要函数中原有参数外,还需要一些参数用来作为判断条件或者其他用处,但是无论outer还是inner中的形参都是固定的,不能改变。那么有下列的解决方案

   ①outer内添加变量。

   ②再套一层,让outer变成闭包,把外层的形参,作为内层的参数。

 

posted @ 2019-04-02 16:52  Mr-Bear  阅读(94)  评论(0编辑  收藏  举报