Python3 装饰器

2024-08-14 18:31 更新

什么是装饰器

装饰器(decorators)是 一种设计模式,之所以被称为装饰器,是因为其本质是对一个函数或者对象进行了"修饰",装饰器可以在你进入对象或者函数前后进行一些操作,可以在不修改原来的代码的情况下让函数的行为发生改变。

面向切面编程(AOP),是指某些业务流程具有相似性,只有部分核心代码有差异,我们可以把相同部分的代码提取出来,不同的代码(切片)另外实现,这就是面向切面编程。在 java 的 spring 中,这是一种很重要的技术,而 python 也有类似的实现方式,最常见的就是装饰器。


装饰器的功能

装饰器允许你动态地修改函数或类的行为。它本质上是一个函数,可以接受一个函数作为参数,并返回一个新的函数或者修改原来的函数。


装饰器语法

装饰器虽然是一个函数,但它更常见的使用方式是使用 ​@decorator_name​ 来应用在函数或方法上。

例如:

from flask import Flask
app =Flask(__name__)

# app.route就是一个Flask内置的装饰器
@app.route("index")
def index():
   return "hello"

Python 还提供了一些内置的装饰器,比如 ​@staticmethod​ 和 ​@classmethod​,用于定义静态方法和类方法。


装饰器的应用场景:

  • 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
  • 性能分析: 可以使用装饰器来测量函数的执行时间。
  • 权限控制: 装饰器可用于限制对某些函数的访问权限。
  • 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。


简单案例

import time
def runtime(func):#定义一个装饰器函数叫runtime 他接受一个函数func作为参数
 def wrapper(*args, **kwargs): # 包装函数wrapper,这里接受所有参数
   
   # 这里是在调用原始函数前添加的新功能
   starttime= time.time() #获取开始执行的时间
   
   # 在包装函数中调用原始函数(可以不调用)
   result = func(*args, **kwargs) # 函数传递的参数为包装函数接受的所有参数
   
  # 这里是在调用原始函数后添加的新功能
   endtime= time.time() #获取结束执行的时间

   print(f"函数{func.__name__}运行时间:{endtime-starttime}秒") #输出函数运行的时间

   return result # 返回函数运行的结果
 return wrapper

# 使用装饰器装饰一个函数,现在运行这个函数时,会运行装饰器的代码,然后再由装饰器运行这个func
# (如果装饰器不调用这个函数,而是调用别的函数,就可以替换掉原有函数的功能)
@runtime
def sleepfunc(arg1, arg2):
 time.sleep(2)
 return arg1 + arg2

print(sleepfunc(1, 2))
函数sleepfunc运行时间:2.005006790161133秒
3

解析:​runtime​是一个装饰器函数,它接受一个函数 ​func​ 作为参数,并返回一个内部函数 ​wrapper​,在 ​wrapper​ 函数内部,你可以执行一些额外的操作,然后调用原始函数 ​func​,并返回其结果。

  • runtime 是装饰器,它接收一个函数 func 作为参数。
  • wrapper 是内部函数,它是实际会被调用的新函数,它包裹了原始函数的调用,并在其前后增加了额外的行为。
  • 当我们使用 ​@runtime​ 前缀在 ​sleepfunc​ 定义前,Python会自动将 ​sleepfunc​ 作为参数传递给 ​runtime​,然后将返回的 ​wrapper​ 函数替换掉原来的 ​sleepfunc​。


使用装饰器

装饰器通过 ​@​ 符号应用在函数定义之前,例如:

@time_logger
def target_function():
  pass

等同于:

def target_function():
  pass
target_function = time_logger(target_function)

这会将 ​target_function​ 函数传递给 ​decorator​ 装饰器,并将返回的函数重新赋值给 ​target_function​。从而,每次调用 ​target_function​ 时,实际上是调用了经过装饰器处理后的函数。

通过装饰器,开发者可以在保持代码整洁的同时,灵活且高效地扩展程序的功能。


带参数的装饰器

装饰器函数也可以接受参数,但是需要在原有的装饰器外再套一层装饰器。

例如:

实例

import time



def runtime(n:int):#定义一个装饰装饰器的装饰函数,他可以接受参数
 def decorator(func):#定义一个装饰器函数,接受一个函数func作为参数
   def wrapper(*args, **kwargs): # 包装函数wrapper,这里接受所有参数
   
       # 这里是在调用原始函数前添加的新功能
       starttime= time.time() #获取开始执行的时间
   
       # 在包装函数中调用原始函数(可以不调用)
       result = func(*args, **kwargs) # 函数传递的参数为包装函数接受的所有参数
   
       # 这里是在调用原始函数后添加的新功能
       endtime= time.time() #获取结束执行的时间

       print(f"函数{func.__name__}运行时间:{endtime-starttime}秒") #输出函数运行的时间
       print("装饰器传递过来的参数为:",n)

       return result # 返回函数运行的结果
   return wrapper # 返回包装函数
 return decorator # 返回装饰器函数


# 使用装饰器装饰一个函数,现在运行这个函数时,会运行装饰器的代码,然后再由装饰器运行这个func
# (如果装饰器不调用这个函数,而是调用别的函数,就可以替换掉原有函数的功能)
@runtime(1)
def sleepfunc(arg1, arg2):
 time.sleep(2)
 return arg1 + arg2

print(sleepfunc(1, 2))

运行结果为:

函数sleepfunc运行时间:2.000138521194458秒
装饰器传递过来的参数为: 1
3

以上代码中 ​runtime​函数是一个带参数的装饰器,它接受一个整数参数 ​n​,然后返回一个装饰器函数。该装饰器函数内部定义了 ​wrapper​ 函数,我们可以在装饰器内部获取到这个参数n(内层函数可以获取到外层函数的变量),上面只是简单的打印了这个参数,我们也可以让这个参数参与到代码运行中。

比如​flask​的​@app.route​就可以接受参数来决定该方法是​get​还是​post​。


类装饰器

除了函数装饰器,Python 还支持类装饰器。类装饰器是包含 ​call​ 方法的类,它接受一个函数作为参数,并返回一个新的函数。

实例

class DecoratorClass:
 def __init__(self, func):
   self.func = func

 def __call__(self, *args, **kwargs):
   # 在调用原始函数之前/之后执行的代码
   result = self.func(*args, **kwargs)
   # 在调用原始函数之后执行的代码
   return result
   
@DecoratorClass
def my_function():
   pass
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号