当前位置: 首页 > news >正文

Python装饰器函数《最详细》

装饰器函数

函数作为参数传递

在python当中一切皆对象,函数也可以当成对象一样被调用和传递

  • 函数没有使用小括号,可以将其当做一个变量传递,如:greet = hi
  • 函数使用小括号调用,则该函数会立马执行并返回结果,如greet = hi()
def hi(name="yasoob"):return "hi " + nameprint(hi())
# output: 'hi yasoob'# 我们甚至可以将一个函数赋值给一个变量,比如
greet = hi
# 我们这里没有在使用小括号,因为我们并不是在调用hi函数
# 而是在将它放在greet变量里头。我们尝试运行下这个print(greet())
# output: 'hi yasoob'# 如果我们删掉旧的hi函数,看看会发生什么!
del hi
print(hi())
#outputs: NameErrorprint(greet())
#outputs: 'hi yasoob'

函数作为参数传递给另一个函数

def hi():return "hi yasoob!"def doSomethingBeforeHi(func):print("I am doing some boring work before executing hi()")print(func())doSomethingBeforeHi(hi) # 可以发现hi没有带小括号,不会返回结果,而是将改函数作为参数传递给函数doSomethingBeforeHi,当执行doSomethingBeforeHi()时才会执行里面的回调函数hi()

可以发现hi没有带小括号,不会返回结果,而是将改函数作为参数传递给函数doSomethingBeforeHi,当执行doSomethingBeforeHi()时才会执行里面的回调函数hi()

装饰器原理

其实装饰器本质上也就是将函数作为参数传入给另一个函数。

def outerfunc(func):def innerfunc():print("before func")func()print("after func")return innerfunc()def func_a():print("func_a")result = outerfunc(func_a) # 这里func_a只是作为一个参数传递给outerfunc,并不会立马执行
result() # 等价于outerfunc(func_a)(),调用函数执行结果:
before func
func_a
after func

outerfunc(func_a)传递函数,可以使用装饰器简化

def outerfunc(func):def innerfunc():print("before func")func()print("after func")return innerfunc()@outerfunc # 由于装饰器不需要加参数,因此不用加括号
def func_a():print(func_a.__name__)func_a()
结果:
before func
innerfunc # 不符合预期
after func

但是我们调用func_a().__name__,返回的却是innerfunc,不符合我们预期,我们希望返回被装饰函数本身的函数名。

Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps

我们定义装饰器时使用@wraps装饰器,用于不改变函数本身的性质,改进如下:

def outerfunc(func):@wraps(func)def innerfunc():print("before func")func()print("after func")return innerfunc()@outerfunc # 由于装饰器不需要加参数,因此不用加括号
def func_a():print(func_a.__name__)func_a()
结果:
before func
func_a # 返回本身的函数名
after func

装饰器传入参数

如果被装饰的函数携带参数,我们需要在装饰器函数内声明*args, **kwargs用于接收被装饰函数的入参。

普通装饰器

def outerfunc(func):@wraps(func)def innerfunc(*args, **kwargs):print("before func")func(*args, **kwargs) # 函数内部接收传入函数的参数print("after func")return innerfunc()@outerfunc
def func_a(name):print(f"Hello, {name}!")func_a("Alice")
结果:
before func
Hello, Alice
after func

由于装饰器本身就是函数本身,因此装饰器也具有接受参数的功能。

带参数的装饰器

def repeat(num_times):def outerfunc(func):@wraps(func)def innerfunc(*args, **kwargs):for _ in range(num_times):func(*args, **kwargs)print("after func")return innerfunc()@outerfunc(3) # 装饰器接收参数
def func_a(name):print(f"Hello, {name}!")func_a("Alice")
结果:
before func
Hello, Alice
Hello, Alice
Hello, Alice
after func
  • repeat 是一个接收参数的装饰器工厂函数,它返回一个真正的装饰器 outerfunc

多个装饰器调用

from functools import wrapsdef outerfunc1(func):@wraps(func)def innerfunc(*args, **kwargs):print("before outerfunc1")func(*args, **kwargs)  # 函数内部接收传入函数的参数print("after outerfunc1")return innerfunc  # 注意这里返回的是 innerfunc,而不是调用它def outerfunc2(func):@wraps(func)def innerfunc(*args, **kwargs):print("before outerfunc2")func(*args, **kwargs)  # 函数内部接收传入函数的参数print("after outerfunc2")return innerfunc  # 同样,返回的是 innerfunc@outerfunc2
@outerfunc1  # 执行顺序为 outerfunc1 -> outerfunc2 -> func_a
def func_a(name):print(f"Hello, {name}!")func_a("Alice")
输出结果:
before outerfunc2
before outerfunc1
Hello, Alice!
after outerfunc1
after outerfunc2

由此可见,装饰器的执行顺序为:

  1. outerfunc2innerfunc 被调用,打印 "before outerfunc2"
  2. 接下来是调用 outerfunc1innerfunc,这个 innerfunc 执行的顺序为:
    • 打印 "before outerfunc1"
    • 调用原始的 func_a,因此打印 "Hello, Alice!"
    • 然后 outerfunc1 的代码继续执行,打印 "after outerfunc1"
  3. 最后,控制返回到 outerfunc2innerfunc,执行完毕后,打印 "after outerfunc2"

换句话说,就是最内层的装饰器优先应用于func,然后装饰器由内到外依次被调用。因此装饰器的调用顺序其实是“自下而上”的或“自内而外”

装饰器作用

装饰器: 主要用于增强或修改被装饰函数的行为,而不需要直接修改函数本身的代码,并且调用灵活。使用装饰器能够非常清晰地表达出代码的意图(例如:进行日志记录、性能监测、权限检查等)。

使用装饰器记录日志

def log(logfile='out.log'):def logging_decorator(func):@wraps(func)def wrapped_function(*args, **kwargs):log_string = func.__name__ + " was called"print(log_string)# 打开logfile,并写入内容with open(logfile, 'a') as opened_file:# 现在将日志打到指定的logfileopened_file.write(log_string + '\n')return func(*args, **kwargs)return wrapped_functionreturn logging_decorator@log()
def myfunc1():passmyfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
http://www.dtcms.com/a/315389.html

相关文章:

  • 06 基于sklearn的机械学习-欠拟合、过拟合、正则化、逻辑回归、k-means算法
  • 深度残差网络ResNet结构
  • 补:《每日AI-人工智能-编程日报》--2025年7月30日
  • 第二十四天(数据结构:栈和队列)队列实践请看下一篇
  • 数据集相关类代码回顾理解 | np.mean\transforms.Normalize\transforms.Compose\xxx.transform
  • MySQL中COUNT(\*)、COUNT(1)和COUNT(column),到底用哪个?
  • 8.4 Codeforces练习
  • 库克宣布苹果ALL-IN AI战略:效仿iPhone模式实现弯道超车 | AI早报
  • 机器学习——基本算法
  • LangChain4j + Milvus 从0-1实现会话管理与RAG检索的AIChat超详细教程
  • 数据结构——队列(Queue)
  • Linux学习—数据结构(链表2)
  • 红队信息收集工具oneforall子域名搜集爆破工具安装使用教程详细过程
  • 句子表征-文本匹配--representation-based/interactive-based
  • 【华为机试】685. 冗余连接 II
  • 补:《每日AI-人工智能-编程日报》--2025年7月28日
  • 【深度学习新浪潮】近三年零样本图像分类研发进展调研
  • mongodb 和 mysql 相关操作
  • 【C++】语法基础篇
  • 厄米系统(Hermitian System)
  • 【大模型05】Embedding和向量数据库
  • 【测试】⾃动化测试概念篇
  • 用户与组管理命令
  • python算法【楼梯数量计算】
  • Hadoop HDFS 3.3.4 讲解~
  • linux的用户操作(详细介绍)
  • 牛客笔试题错题整理(1)
  • Field and wave electromagnetics 复习
  • 【编程实践】点云曲率计算与可视化
  • Pimpl惯用法