「Python学习笔记」Python修饰器

如何在不更改原函数的情况下,扩展函数功能

例如:在不了解一个函数实现时,往往在函数前后加上日志log

def black_box(func):
  """
  不了解这个函数的具体实现,只知道函数会返回一个整数
  将函数视为黑箱
  """
  return 10

def black_box_with_log(func):
  """测试黑箱"""
  print("==========Start==========")
  print(black_box(func))
  print("==========End==========")

这种方法的缺点

  • 工作量大

    对每一个不同的黑盒函数进行类似的扩展,都需要编写特定的拓展函数

  • 代码冗余

修饰器Decorator

python修饰器就是用于拓展原来函数功能的一种函数

这个函数的特殊之处在于它的返回值也是一个函数

这一点像C++的函数指针

使用python修饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能

修饰器适用于一类函数,可以统一地对这些函数进行类似的扩展

简单修饰器

from functools import wraps

def test(func):
  """函数测试修饰器"""
  @wraps(func)
  def wrapper(*args, **kwargs):
    print("==========Start==========")
    func(*args, **kwargs)
    print("==========End==========")
  return wrapper

@test
def hello():
  print("Hello world!")

其中,wrapper为闭包函数,返回一个对输入函数func进行包装后的新函数

wrapper函数执行的任务就是新建一个测试函数,并在测试函数中对原函数进行调用。

形象的比喻就是给糖果包上一层糖纸

⚠️@是python修饰器的语法糖,其作用等同于hello = test(hello)

使用@语句的结果就是不用再像上一节中定义一个black_box_with_log来调用包装后的函数。

其结果类似于在上一节中增加语句black_box = blac_box_with_log

⚠️使用@将导致hello函数在形式上变为名为test的函数,hello.__name__的结果变为test

在wrapper函数前使用@wrap修饰,能够把原函数的元信息拷贝到装饰器函数中,使得装饰器函数也有和原函数一样的元信息

带参数的修饰器

修饰器函数也是函数,也是能传参的

from functools import wraps

def testUtil(type):
  def log(func):
    """函数测试修饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
      print("==========%s Start==========" % type)
      func(*args, **kwargs)
      print("==========%s End==========" % type)
    return wrapper
  return log

@testUtil(type="Log")
def hello():
  print("Hello world!")

testUtil是对log修饰器的包装,并根据参数返回一个修饰器

修饰器类

类能够将一组功能相似的函数包装在一起,能够管理更复杂的业务逻辑,具有灵活度大、高内聚、封装性等优点。

from functools import wraps

class log(object):
  def __init__(self, message):
    self.message = message
  def __call__(self, func):
    @wraps(func)
    def wrapper(*args, **kwargs):
      print("==========%s Start==========" % self.message)
      func(*args, **kwargs)
      print("==========%s End==========" % self.message)
    return wrapper
  
  
@log('Test')
def hello():
  print("Hello World!")

在修饰器类中,增加了__call__函数。在使用@语法糖将修饰器类附加到函数上时,会自动调用__call__方法

多个修饰器叠加的顺序

@test
@plus2
@plus1
def func():
  pass

等效于func = test(plus2(plus1(func))),即对func函数进行两次扩展后,再进行测试。

posted @ 2021-05-07 13:42  VanGy  阅读(214)  评论(0编辑  收藏  举报