Python @函数装饰器及用法(超级详细)转

2023-10-26

Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod、classmethod 和 property 都是 Python 的内置函数。那么,我们是否可以开发自定义的函数装饰器呢?

答案是肯定的。
当程序使用“@函数”(比如函数 A)装饰另一个函数(比如函数 B)时,实际上完成如下两步:
1.将被修饰的函数(函数 B)作为参数传给 @ 符号引用的函数(函数 A)。
2.将函数 B 替换(装饰)成第 1 步的返回值。

从上面介绍不难看出,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值)。其实所谓的装饰器,就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

为了让大家厘清函数装饰器的作用,下面看一个非常简单的示例:

def funA(fn):
    print('A')
    fn() # 执行传入的fn参数
    return 'fkit'
'''
下面装饰效果相当于:funA(funB),
funB 将会替换(装饰)成 funA() 语句的返回值;
由于funA()函数返回 fkit,因此 funB 就是 fkit
'''
@funA
def funB():
    print('B')
print(funB) # fkit
===================
A
B
fkit

上面程序使用 @funA 修饰 funB,这意味着程序要完成两步操作:
1.将 funB 作为 funA() 的参数,也就是上面代码中 @funA 相当于执行 funA(funB)
2.将 funB 替换成 funA() 执行的结果,funA() 执行完成后返回 fkit,因此 funB 就不再是函数,而是被替换成一个字符串。

其实,简单地理解函数装饰器的作用,上面程序可以等价地转换成如下程序:

def funA(fn):
    print('A')
    fn() # 执行传入的fn参数
    return 'fkit'
def funB():
    print('B')
funB = funA(funB)
print(funB) # fkit
===================
A
B
fkit

注意,此程序中的 funB = funA(funB) 就等同于上面程序中 @funA 所起的作用。

这个函数装饰器导致被修饰的函数变成了字符串,那么函数装饰器有什么用?
被修饰的函数总是被替换成 @ 符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于 @ 符号所引用的函数的返回值决定,换句话说,如果 @ 符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。

带参数的函数装饰器

如果原函数 funB() 中有参数需要传递给函数装饰器,应该如何实现?

一个简单的办法是,可以在对应的函数装饰器 funA() 上,添加相应的参数,例如:

def foo(fn):
    # 定义一个嵌套函数
    def bar(a):  
        fn(a * (a - 1))
        print("*" * 15)
        return fn(a * (a - 1))
    return bar
'''
下面装饰效果相当于:foo(my_test),
my_test将会替换(装饰)成该语句的返回值;
由于foo()函数返回bar函数,因此my_test就是bar
同时,my_test 的参数 a 对应 bar 函数的参数 a
'''
@foo
def my_test(a):
    print("==my_test函数==", a)
# 打印my_test函数,将看到实际上是bar函数
print(type(my_test),my_test)
# 下面代码看上去是调用my_test(),其实是调用bar()函数
my_test(10)
==============
<class 'function'> <function foo.<locals>.bar at 0x0000000002B59268>
==my_test函数== 90
***************
==my_test函数== 90

上面程序定义了一个装饰器函数 foo,该函数执行完成后并不是返回普通值,而是返回 bar 函数(这是关键),这意味着被该 @foo 修饰的 my_test() 函数最终都会被替换成 bar() 函数。

上面程序使用 @foo 修饰 my_test() 函数,因此程序同样会执行 foo(my_test),并将 my_test 替换成 foo() 函数的返回值:bar 函数。所以,上面程序第 18 行代码在打印 my_test 函数时结果为:

<class 'function'> <function foo.<locals>.bar at 0x0000000002B59268>

实际上输出的是 bar 函数,这说明 my_test 已经被替换成 bar 函数。接下来程序调用 my_test() 函数,实际上就是调用 bar() 函数。

在此基础上,还有一个问题,如果程序中另外还有一个函数,也需要使用 funA 装饰器,但是这个新的函数有 2 个参数,此时又该怎么办呢?例如:
纯文本复制

@foo
def new_test(a,b):
    ....

在这种情况下,最简单的解决方式是用 *args 和 **kwargs 作为 foo 函数装饰器内部函数 bar() 的参数,*args 和 **kwargs 表示接受任意数量和类型的参数,因此函数装饰器可以写成下面的形式:

def foo(fn):
    # 定义一个嵌套函数
    def bar(*args, **kwargs):
        fn(*args, **kwargs)
    return bar

@foo
def my_test(a):
    print("==my_test函数==", a)

@foo
def new_test(a, b):
    print("==new_test函数==", a, " ", b)

print(my_test)  #<function foo.<locals>.bar at 0x0000000002AA9268>
my_test(10) # ==my_test函数== 10
print(new_test) #<function foo.<locals>.bar at 0x0000000002AA9378>
new_test(5, 6) # ==new_test函数== 5   6

结果:

<function foo.<locals>.bar at 0x0000000000829268>
==my_test函数== 10
<function foo.<locals>.bar at 0x0000000000829378>
==new_test函数== 5   6

带自定义参数的函数装饰器

其实,函数装饰器还有更大程度的灵活性。刚刚说了,装饰器可以接受原函数任意类型和数量的参数,除此之外,它还可以接受自己定义的参数。

举个例子,比如要定义一个参数,来表示装饰器内部函数被执行的次数,那么就可以写成下面这种形式:

def foo(num):
    def my_decorator(fn):
        def bar(*args, **kwargs):
            for i in range(num):
                fn(*args, **kwargs)
        return bar
    return my_decorator

@foo(3)
def my_test(a):
    print("==my_test函数==", a)

@foo(5)
def new_test(a, b):
    print("==new_test函数==", a, " ", b)

print(my_test) # <function foo.<locals>.my_decorator.<locals>.bar at 0x00000000020892F0>
my_test(10)
print(new_test) # <function foo.<locals>.my_decorator.<locals>.bar at 0x0000000002089400>
new_test(6, 5)

结果

<function foo.<locals>.my_decorator.<locals>.bar at 0x00000000020892F0>
==my_test函数== 10
==my_test函数== 10
==my_test函数== 10
<function foo.<locals>.my_decorator.<locals>.bar at 0x0000000002089400>
==new_test函数== 6   5
==new_test函数== 6   5
==new_test函数== 6   5
==new_test函数== 6   5
==new_test函数== 6   5
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python @函数装饰器及用法(超级详细)转 的相关文章

  • Python Serial串口的简单数据收发

    导入模块 注意这里模块名是pyserial pip install pyserial 1 打开串口 import serial com serial Serial COM3 9600 print com 2 发送数据 import seri
  • Python基础——函数的基本使用

    文章目录 一 定义 二 属性 三 操作 1 函数的参数 2 函数返回值 3 函数多个返回值 4 多个return语句 5 函数调用函数 6 函数的联系及注释 7 局部变量和全局变量 8 数据的传递 9 lambda表达式 一 定义 1 语法
  • .numpy()、.item()、.cpu()、.clone()、.detach()及.data的使用 && tensor类型的转换

    文章目录 numpy item cpu clone detach 及 data的使用 item cpu numpy clone detach data data和 detach 不同点 Tensor类型的转换 numpy item cpu
  • Python基础-48-文本处理(逗号分隔值CSV)

    前言 python自带模块csv可以将数据以csv格式输出到文件 也可以将csv数据读回 列表 元组数据写入和读取 代码部分 coding utf 8 import csv data也可以为列表 data 1 suner001 b12345
  • 如何在Anaconda安装Pygame

    开始之前 先来安装Pygame 可使用pip模块来帮助下载安装python包 要安装Pygame 需在终端提示符下执行如下命令 python m pip install user pygame 对于下载了anaconda用户 可按以下操作
  • 题目描述已知矩形的长和宽,计算其周长和面积。

    1010 矩形的周长和面积 1级 时间限制 1 Sec 内存限制 128 MB 提交 777 解决 442 提交 状态 讨论版 命题人 admin 题目描述 已知矩形的长和宽 计算其周长和面积 输入 仅一行 用空格隔开的两个整数 表示矩形的
  • Python-字符串的世界

    Python字符串的世界 在Python编程中 字符串 String 是一种非常重要的数据类型 用于表示文本信息 字符串可以包含字母 数字 特殊字符以及空格 它们在编程中被广泛用于文本处理 格式化输出 字符串匹配等各种场景 创建字符串 创建
  • 【Python 1-10】Python手把手教程之——一篇讲透if语句以及if语句的特殊用法

    作者 弗拉德 来源 弗拉德 公众号 fulade me if 简单示例 假设你有一个汽车列表 并想将其中每辆汽车的名称打印出来 对于大多数汽车 都应以首字母大写的方式打印其名称 但对于汽车名 bmw 应以全大写的方式打印 下面的代码遍历一个
  • 自学笔记-Python基础09--第三方库的概念及操作

    库 具有相关功能模块的集合 python的一大特色就是拥有强大的库 库可以分为三种 1 标准库 python自带的 无需安装直接使用 2 第三方库 由他人提供的 使用时需要先安装 3 自定义库 自己写的模块 自己用 标准库 想看python
  • Python的10个常用代码简写技术

    今天我给大家整理了一份10个程序员常用的代码简写技术 看懂一种是入门 全懂就是大神 你能知道几个呢 1 三元操作符 当想写if else语句时 使用三元操作符来代替 const x 20 let answer if x gt 10 简写 c
  • 2023年IT行业就业前景分析,准职场人必看!

    随着疫情的放开 2022已接近尾声 新的一年即将来临 作为打工人最关心的肯定是2023年的就业市场以及行业未来发展前景 如何最直观地看待这个行业是否还有前景 最好的方式就是看市场需求 作为准职场人的你 速速关注起来 根据智联招聘10月发布的
  • 进程,线程,协程总结

    进程 三种状态 就绪态 运行的条件都已经慢去 正在等在cpu执行 执行态 cpu正在执行其功能 等待态 等待某些 条件满足 例如一个程序sleep了 此时就处于等待态 生命周期 用户编写代码 代码本身是以进程运行的 启动程序 进入进程 就绪
  • 学完Python,怎么变现?小哥哥10000元外快了解一下

    自学 Python 之后如果不去公司上班 自己一个人可以通过此技能挣什么钱 逆天的Python 只要你掌握了相关技术 就可以靠它赚钱 具体怎么赚 我们来看看一位小哥哥的回答 以我差不多四年的 Python 使用经验来看 大概可以按以下这些路
  • python---三元表达式

    三元表达式适用于二选一的场景 其结构为 值1 if 条件 else 值2 条件如果成立则使用值1 if前面的数据 条件如果不成立则使用值2 else后面的数据 写一个电影系统 需要决定电影是否收费 is change input 是否收费
  • 保姆级python入门教程(非常详细),从零基础入门到精通,从看这篇开始!

    一 初聊Python 文末有惊喜福利 1 为什么要学习Python 在学习Python之前 你不要担心自己没基础或 脑子笨 我始终认为 只要你想学并为之努力 就能学好 就能用Python去做很多事情 在这个喧嚣的时代 很多技术或概念会不断兴
  • 【Python】PyCharm中调用另一个文件的函数或类

    欢迎来到Python专栏 PyCharm中调用另一个文件的函数或类 o o 嗨 我是小夏与酒 博客主页 小夏与酒的博客 该系列文章专栏 Python学习专栏 文章作者技术和水平有限 如果文中出现错误 希望大家能指正 欢迎大家关注 目录 Py
  • 高考失利,还适合选计算机专业吗??

    前言 高考落榜 人生陷入低谷 对于很多人来说 这意味着梦想的破灭和无尽的绝望 但是 对于我来说 这只是人生旅程的一个起点 我喜欢编程也热爱编程 虽然网上很多言论说计算机行业已经很卷了 但是我却认为无论再哪个行业 你不卷 也同样落后于人 所以
  • python--- end=“ , 单独的一行print()是什么意思

    有如下一道练习题 编写代码打印出下列图形 代码如下 for i in range 4 for j in range 5 print end print 其中end 意思是为末尾end传递一个空字符串 这样print函数不会在字符串末尾添加一
  • Python程序异常处理

    一 什么是异常 异常就是程序运行时发生错误的信号 在程序由于某些原因出现错误的时候 若程序没有处理它 则会抛出异常 程序也的运行也会随之终止 程序异常带来的问题 1 程序终止 无法运行下去 2 如果程序是面向客户 那么会使客户的体验感很差
  • 4.函数、模块与包

    文章目录 一 函数 二 模块与包 引用 一 函数 Python 使用 def 关键字来声明函数 格式如下所示 def 函数名 参数 函数体 return 返回值 如果要定义一个无任何功能的空函数 函数体只写 pass 即可 def 函数名

随机推荐

  • std::condition_variable

    std condition variable std condition variable 是C 11提供的条件变量 可用于同时阻塞一个线程或多个线程 一般的 生产者线程利用支持std mutex的std lock guard std un
  • 【React Hook】一文让你彻底明白何为State Hook?

    使用 State Hook 下面的例子介绍了 Hook import React useState from react function Example 声明一个叫 count 的 state 变量 const count setCoun
  • 如何解除计算机的启动项,UEFI安全启动怎么关闭 关闭UEFI启动项的方法图解

    大家都知道现在很多电脑都预装win8系统 其系统都开启了UEFI安全启动选项 然而 对于不习惯win8操作界面的朋友来说 可能就会把win8改为win7 但是我们得知道Win8改装Win7需要在BIOS下关闭UEFI选项 如果OS选项已经关
  • ctfshow-萌赛

    目录 web 签到 给她 假声赛 web 签到 很明显的命令执行漏洞 我们把前后闭合即可 payload 1 ls 1 1 cat flag 1 给她 根据题目提示很容易就想到是 git泄露 直接用gitHack扫描题目地址 git 发现存
  • 电子科技大学人工智能期末复习笔记(二):MDP与强化学习

    目录 前言 期望最大搜索 Expectimax Search 马尔科夫决策 MDP offline 超重点 先来看一个例子 基本概念 政策 Policy 折扣 Discounting 如何停止循环 价值迭代 Value Iteration
  • LeetcodeSQL入门——知识点总结(选择/排序/修改/字符串处理/正则)

    LeetcodeSQL入门 选择 排序 修改 字符串处理 选择 sql语言对于空值的判断是IS NULL或者IS NOT NULL eg 某网站包含两个表 Customers 表和 Orders 表 编写一个 SQL 查询 找出所有从不订购
  • 剑指Offer 40

    使用优先队列 将非负数变为非正数存储 结果变成非负数 class Solution public int getLeastNumbers int arr int k if k 0 return new int 0 int nums new
  • SequenceInputStream----合并流

    这个类的作用是将多个输入流合并成一个输入流 通过SequenceInputStream类包装后形成新的一个总的输入流 1 SequenceInputStream InputStream s1 InputStream s2 和Sequence
  • 差分方程与滤波的实现

    1 滤波基础知识 2 差分方程 3 IIR滤波器 1 直接I型IIR滤波器 2 直接II型IIR滤波器
  • 备战2023蓝桥国赛-移动服务

    题目描述 解析 这道题我想复杂了 一开始我是这样想的 设dp i j 表示按顺序满足到第i个请求时 最初在j号点的人到达第i个请求的位置的情况下的最小花费 state i j 表示按顺序满足到第i个请求时 最初在j号点的人到达第i个请求的位
  • Typescript 之接口 interface(详解)

    一 interface的基本含义 TS新增了一个重要概念 接口 分为对象类型接口和函数类型接口 接口可以约束对象 函数 类的结构和类型 是一种代码协作必须遵守的契约 Interface 是一种描述对象或函数的东西 你可以把它理解为形状 一个
  • 使用github免费搭建个人博客后的写作及上传说明

    项目地址 我的博客github项目地址 个人博客网站 欢迎进入我的博客 本篇文章介绍如何在博客搭建好的基础上 使用它 使用它来写博客 更新博客页面 第一种方法 本地编辑 上传到github 首先 从我的github个人博客项目仓库克隆出该工
  • vscode中CommandNotFoundError: Your shell has not been properly configured to use ‘conda active的可能解决方法

    一 问题背景 如题 出现该问题后 我首先通过搜索尝试了conda init 用管理员身份运行powershell进行权限配置等方法无果 python之 vscode中激活conda虚拟环境报错或者打开虚拟环境失败 if using cond
  • 滑动时间窗口的思想和实现,环形数组,golang

    固定时间窗口 在开发限流组件的时候 我们需要统计一个时间区间内的请求数 比如以分钟为单位 所谓固定时间窗口 就是根据时间函数得到当前请求落在哪个分钟之内 我们在统计的时候只关注当前分钟之内的数量 即 0s 60s 因为流量并不是均匀的 所以
  • 微软RIA服务2009年7月预览版官方手册第2节(翻译:戴石麟)

    2 理解N层Silverlight应用项目 微软 NET RIA服务通过结合ASP NET和Silverlight平台来简化传统N层应用模式 应用逻辑写在中间层上 通过查询 更新 定制方法和服务操作来控制对数据的访问 NET RIA服务特性
  • Verilog入门学习笔记:Verilog基础语法梳理

    无论是学IC设计还是FPGA开发 Verilog都是最基本 最重要的必备技能 但任何一门编程语言的掌握都需要长期学习 并不是简简单单的随便读几本书 随便动动脑筋那么简单 Verilog是一门基于硬件的独特语言 由于它最终所实现的数字电路 具
  • netstat详解

    netstat是控制台命令 是一个监控TCP IP网络的非常有用的工具 它可以显示路由表 实际的网络连接以及每一个网络接口设备的状态信息 netstat可以用于显示与IP TCP UDP和ICMP协议相关的统计数据 一般用于检验本机各端口的
  • vb wor转存html,利用VB操作WORD的基本方法

    利用VB操作WORD的基本方法 通过查阅资料 自我实践 经实验通过 先引用word Application Dim MyWord As Word Application Dim MyWordBook As Word Document Set
  • Numpy:基础数据结构

    1 数组的基本属性 import numpy as np ar np array 1 2 3 4 5 6 7 print ar 输出数组 注意数组的格式 中括号 元素之间没有逗号 和列表区分 print ar ndim 输出数组维度的个数
  • Python @函数装饰器及用法(超级详细)转

    Python 内置的 3 种函数装饰器 分别是 staticmethod classmethod 和 property 其中 staticmethod classmethod 和 property 都是 Python 的内置函数 那么 我们