深夜好文

2023-11-19

题图:https://unsplash.com/@oldskool2016

无论项目中还是面试都离不开装饰器话题,装饰器的强大在于它能够在不修改原有业务逻辑的情况下对代码进行扩展,权限校验、用户认证、日志记录、性能测试、事务处理、缓存等都是装饰器的绝佳应用场景,它能够最大程度地对代码进行复用。

但为什么初学者对装饰器的理解如此困难,我认为本质上是对Python函数理解不到位,因为装饰器本质上还是函数

函数的定义

理解装饰器前,需要明白函数的工作原理,我们先从一个最简单函数定义开始:

def foo(num):
    return num + 1

上面定义了一个函数,名字叫foo,也可以把 foo 可理解为变量名,该变量指向一个函数对象

0?wx_fmt=png

调用函数只需要给函数名加上括号并传递必要的参数(如果函数定义的时候有参数的话)

value = foo(3)
print(value) # 4

变量名 foo 现在指向 <function foo at 0x1030060c8> 函数对象,但它也可以指向另外一个函数。

def bar():
    print("bar")
foo = bar
foo() # bar

0?wx_fmt=png函数作为返回值

在Python中,一切皆为对象,函数也不例外,它可以像整数一样作为其它函数的返回值,例如:

def foo():
    return 1

def bar():
    return foo

print(bar()) # <function foo at 0x10a2f4140>

print(bar()()) # 1 
# 等价于
print(foo()) # 1

bar() 的返回值是一个函数对象,所以我们可以继续对返回值进行调用,调用bar()()等价于调用 foo(),因为 变量 foo 指向的对象与 bar() 的返回值是同一个对象。

0?wx_fmt=png

函数作为参数

函数还可以像整数一样作为函数的参数,例如:

def foo(num):
    return num + 1

def bar(fun):
    return fun(3)

value = bar(foo)
print(value)  # 4

函数 bar 接收一个参数,这个参数是一个可被调用的函数对象,把函数 foo 传递到 bar 中去时,foo 和 fun 两个变量名指向的都是同一个函数对象 。所以调用 fun(3) 相当于调用 foo(3)。

0?wx_fmt=png

函数嵌套

函数不仅可以作为参数和返回值,函数还可以定义在另一个函数中,作为嵌套函数存在,例如:

def outer():
    x = 1
    def inner():
        print(x)
    inner()

outer() # 1

inner做为嵌套函数,它可以访问外部函数的变量,调用 outer 函数时,发生了3件事:

  1. 给 变量 x 赋值为1

  2. 定义嵌套函数 inner,此时并不会执行 inner 中的代码,因为该函数还没被调用,直到第3步

  3. 调用 inner 函数,执行 inner 中的代码逻辑。

闭包

再来看一个例子:

def outer(x):
    def inner():
        print(x)

    return inner
closure = outer(1)
closure() # 1

同样是嵌套函数,只是稍改动一下,把局部变量 x 作为参数了传递进来,嵌套函数不再直接在函数里被调用,而是作为返回值返回,这里的 closure就是一个闭包,本质上它还是函数,闭包是引用了自由变量(x)的函数(inner)。

装饰器

继续往下看:

def foo():
    print("foo")

上面这个函数这可能是史上最简单的业务代码了,虽然没什么用,但是能说明问题就行。现在,有一个新的需求,需要在执行该函数时加上日志:

def foo():
    print("记录日志开始")
    print("foo")
    print("记录日志结束")

功能实现,唯一的问题就是它需要侵入到原来的代码里面,把日志逻辑加上去,如果还有好几十个这样的函数要加日志,也必须这样做,显然,这样的代码一点都不Pythonic。那么有没有可能在不修改业务代码的提前下,实现日志功能呢?答案就是装饰器。

def outer(func):
    def inner():
        print("记录日志开始")
        func() # 业务函数
        print("记录日志结束")
    return inner

def foo():
    print("foo")

foo = outer(foo) 
foo()

我没有修改 foo 函数里面的任何逻辑,只是给 foo 变量重新赋值了,指向了一个新的函数对象。最后调用 foo(),不仅能打印日志,业务逻辑也执行完了。现在来分析一下它的执行流程。

这里的 outer 函数其实就是一个装饰器,装饰器是一个带有函数作为参数并返回一个新函数的闭包,本质上装饰器也是函数。outer 函数的返回值是 inner 函数,在 inner 函数中,除了执行日志操作,还有业务代码,该函数重新赋值给 foo 变量后,调用 foo() 就相当于调用 inner()

foo 重新赋值前:

0?wx_fmt=png

重新赋值后:

0?wx_fmt=png

另外,Python为装饰器提供了语法糖 @,它用在函数的定义处:

@outer
def foo():
    print("foo")

foo()

这样就省去了手动给foo重新赋值的步骤。

到这里不知你对装饰器理解了没有?当然,装饰器还可以更加复杂,比如可以接受参数的装饰器,基于类的装饰器等等。下一篇可以写写装饰器的应用场景。


iOS & Android 两用赞赏通道,赞赏留个言

640?wx_fmt=png

关注「Python之禅」,提高Python技术转发、点赞是对作者的最大的支持

640?wx_fmt=png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深夜好文 的相关文章

  • scipy.stats的用法——常见的分布和函数

    介绍python统计函数库scipy stats中常见的分布和函数 commom distributions uniform norm poisson bernoulli expon lognorm norm t chi2 f commom
  • mysql中geometry字段的查询和保存

    有段时间为了追求效率 把用es相关的地理位置功能都去掉了 使用mysql已有的geometry的功能做地理位置 mybatis这块 TableName value t event info autoResultMap true 如果使用复杂
  • python根据excel时间表统计24小时各小时区间点的个数

    1 首先使用excel中的HOUR 函数 将日期数据 年 月 日 时 分 秒 转换为小时 表格命名为hour xlsx 2 使用python读取excel数据hour xlsx 将小时列转换为列表hour 将列表hour转换为集合myset
  • Java词频统计算法(使用单词树)

    许多英语培训机构 如新东方 都会出几本 高频词汇 的书 主要内容是统计近几年来各类外语考试中屡次出现的高频词汇 帮助考生减少需要背的生词的数量 但这些高频是如何被统计出来的呢 显然不会用手工去计算 假如我们已经将一篇文章存在一字符串 Str
  • EditText的三种监听内容的方式

    editText addTextChangedListener object TextWatcher override fun onTextChanged s CharSequence start Int before Int count
  • 招聘数据采集+Hive数据分析+数据可视化

    1 需求 背景描述 近年来随着IT产业的快速发展 全国各地对IT 类的人才需求数量也在不断 增多 XHS集团 为了明确今后IT产业人才培养方向 在多个省份进行 IT公 司岗位情况调研分析 你所在的小组将承担模拟调研分析的任务 通过在招聘 网
  • 【Apifox Helper】自动生成接口文档,IDEA+Apifox懒人必备

    文章目录 前言 缘由 接口文档对接爽 整理起来真费脑 本文阅读时长 约10分钟 前置条件 1 IDEA开发工具 2 Apifox 不必要 主要目标 一秒生成接口文档 水图 IDEA中项目接结构图 生成到Apifox接口文档图 试用人群 1
  • PowerDesigner15.1如何安装?汉化破解版安装教程(含安装文件、汉化包、破解文件)

    powerdesigner是能进行数据库设计的强大的软件 是一款开发人员常用的数据库建模工具 使用它可以分别从概念数据模型 Conceptual Data Model 和物理数据模型 Physical Data Model 两个层次对数据库
  • glog详解

    glog详解 1 glog的安装与使用 1 下载glog http code google com p google glog downloads detail name glog 0 3 3 tar gz 2 安装glog configu
  • 请用详细列出宝塔搭建Nacos的流程

    好的 为了在宝塔中搭建 Nacos 服务 您需要执行以下步骤 下载 Nacos 的最新版本并解压缩 使用文本编辑器打开 conf application properties 文件 并修改其中的数据库配置 使 Nacos 可以连接到您的数据
  • C++中队列的顺序存储

    队列 引言 示例 开发环境 运行结果 引言 队列先进先出 队头出队 队尾入队 其存储可以分为顺序存储和链式储存 本文记录队列的顺序存储 也就是队列中的元素存储的内存空间是连续的 这里使用数组来模拟线性队列 示例 直接上代码 SeqQueue
  • java arraylist 拷贝_Copy ArrayList的四种方式

    Copy ArrayList的四种方式 简介 ArrayList是我们经常会用到的集合类 有时候我们需要拷贝一个ArrayList 今天向大家介绍拷贝ArrayList常用的四种方式 使用构造函数 ArrayList有个构造函数 可以传入一
  • 第三周课程总结&实验报告一

    实验报告 1 打印输出所有的 水仙花数 所谓 水仙花数 是指一个3位数 其中各位数字立方和等于该数本身 例如 153是一个 水仙花数 实验代码 public class ShuiXianHua public static void main
  • L3-014 周游世界 (30 分)

    题目 题目链接 题解 DFS 采用的数据结构 vector 索引为起点 值为 终点 起点公司编号 当然你也可以保存终点公司编号 但是代码中的语句就需要改一下了 dfs 传入四个信息 当前节点 遇到的节点数 换乘数 当前节点所在公司的编号 由
  • python 的回调函数

    回调函数就是一个通过函数指针调用的函数 如果你把函数的指针 地址 作为参数传递给另一个函数 当这个指针被用来调用其所指向的函数时 我们就说这是回调函数 有些库函数 library function 却要求应用先传给它一个函数 好在合适的时候
  • 指针(一)

    这里写目录标题 一 什么是指针 二 指针和指针类型 三 野指针 四 指针运算 五 指针和数组 六 二级指针 七 指针数组 一 什么是指针 1 指针是内存中一个最小单元的编号 也就是地址 2 平时我们说的指针 通常指的是指针变量 是用来存放地
  • 大数据手册(Spark)--Spark基本概念

    文章目录 Spark 基本概念 Hadoop 生态 Spark 生态 Spark 基本架构 Spark运行基本流程 弹性分布式数据集 RDD Spark安装配置 Spark基本概念 Spark基础知识 PySpark版 Spark机器学习
  • 用户互动优化:微信营销系统实践

    在当今移动互联网时代 微信已经成为企业进行营销的重要平台之一 而用户互动作为营销的关键环节 对于提升品牌影响力 增强用户忠诚度至关重要 本文将深入探讨如何通过微信营销系统实践 优化用户互动 提升营销效果 建立更紧密的用户关系 1 理解用户互
  • 修改本地host文件加入可用ip使谷歌浏览器翻译插件重新生效

    修改本地host文件加入可用ip使谷歌浏览器翻译插件重新生效 第一步 找到host文件 可以使用这个工具进行对Hosts文件进行一个查找 鼠标放到对应路径上面 点击鼠标右键 选择打开路径就到对应 路径了 也可以复制到这个路径下面去找host
  • .NET6-Asp.Net Core webapi -从零开始的webapi项目

    本项目为本人22年毕设项目 后续会不断更新本篇文章 全部内容都会写在这一篇文章里 喜欢的请持续关注 一 如何创建 Asp Net Core webapi 项目 二 如何使用 EntityFrameWorkCore DbFirst 需要用到的

随机推荐

  • 人工智能 ai基础知识_如何使用人工智能改善基础医疗的成果和效率

    人工智能 ai基础知识 Only 7 percent of a message is based on the words it contains The rest 93 percent comes from the speaker s t
  • 第一个只出现一次的字符(Java)

    题目 在字符串中找出第一个只出现一次的字符 如输入 abaccdeff 则输出 b 第一思路 借助于数组来做 开辟一个长度为26的数组 用来存放字符串中每个字符出现的次数 这样第一次扫描去统计这个字符串中字符出现的次数 第二次去统计第一个出
  • [Leetcode] 3.无重复字符的最长子串

    题目描述 给定一个字符串 找出不含有重复字符的最长子串的长度 示例 给定 abcabcbb 没有重复字符的最长子串是 abc 那么长度就是3 给定 bbbbb 最长的子串就是 b 长度是1 给定 pwwkew 最长子串是 wke 长度是3
  • CUDA复制测试

    这里主要是测试了内存数据读写操作的几种方式 记录了一些测试结果 对于二维数组 10244 1024 4 1 二维线程格 每个线程对应一个元素 2 转换为int2类型 线程宽度减半 3 线程宽度和高度减半 单个线程操作邻近的4个元素 4 线程
  • 使用神经网络对手写体数字图片数据分类(MLP/PCA)

    使用神经网络对手写体数字图片数据分类 MLP PCA 使用sklearn neural network MLPClassifier类实现手写数字图片识别 MLP的常用的几个参数一般为activation 选择激活函数 如relu sigmo
  • 《2023新版JavaWeb开发教程》学习笔记总目录

    本篇文章是本人对于黑马程序员的 2023新版JavaWeb开发教程 的个人向知识点总结归类 用于巩固自身所学知识 以及查阅知识点和相关代码 并尝试养成做笔记的习惯 黑马程序员2023新版JavaWeb开发教程 实现javaweb企业开发全流
  • git status 展示的中文文件乱码

    乱码现象 解决方法 1 打开 项目根目录下的 git config 配置文件 2 在图中位置追加 quotepath false 效果图
  • 权重计算方法三:变异系数法(Coefficient of Variation)

    目录 1 原理简介 2 步骤详解 2 1 原始数据收集 2 2 指标数据正向化 2 3 数据标准化 消除量纲 2 4 计算变异系数 2 5 计算权重及得分 3 案例分析 3 1 获取原始数据 3 2 指标正向化 3 3 数据标准化 3 4
  • 电巢携手武昌工学院工程能力实训顺利开班!

    为深化校企合作 产教融合打造新工科建设 提升学生工程实践能力 电巢工程能力实训班按照不同岗位类别 匹配对应的企业岗位任职能力要求对学生开展分级培养 以产业需求为导向 培养创新型 应用型人才 6月6日下午4时 深圳电巢联合武昌工学院信息工程学
  • IDEA中如何使用debug调试项目 一步一步详细教程

    原文链接 https www linuxidc com Linux 2017 09 146772 htm 在现在的开发中 我们经常采用Debug来追踪代码的运行流程 通常在程序运行过程中出现异常 启用Debug模式可以分析定位异常发生的位置
  • ST-LINK在MDK环境下载hex提示“Internal command error”和“Error:Flash Download failed Cortex-M3”错误的解决办法

    ST LINK V2在MDK环境下 使用SW DP模式下载hex时出现以下错误提示 出现这种错误一般都是ST LINK和目标板SW DP接线时没有将目标板的电源线接到ST LINK插座的TVCC端导致的 解决步骤 1 目标板使用外接电源供电
  • JDK8新特性(二):JDK8接口增强

    1 接口增强 在JDK8之前 JDK规定接口中只能定义 静态常量 抽象方法 修饰词 interface 接口名 静态常量 抽象方法 在JDK8之后 对接口进行了增强 我们可以在接口中定义 静态常量 抽象方法 默认方法 静态方法 修饰词 in
  • CUDA各版本官方下载地址

    一 CUDA各版本官方下载地址 地址 https developer nvidia com cuda toolkit archive 二 说明 备忘 平时找个版本太难找了 转载于 https www cnblogs com songxing
  • Vue2 + 高德地图API 获取用户当前位置等信息,错误default.CitySearch is not a Constructor的应对

    效果 高德地图API参考手册 控制台输出信息 代码 public index html 使用CDN引入高德地图API
  • SQL--查询结果最后加合计行

    union all 是一种 SQL 操作符 用于将两个或多个 SELECT 语句的结果集合并成一个结果集 与 union 不同的是 union all 不会去重 即会保留重复的行 使用 union all 可以方便地将多个表或查询结果合并成
  • 计算机二级【C语言】-复习使用

    文章目录 第一章 C语言概述 1 1 C语言基础知识 1 23题 1 2 常量 变量和数据类型 24 73题 第二章 运算符与表达式 2 1 C语言运算符简介 74 79题 2 2 算术运算符和算数表达式 80 99题 2 3 赋值运算符和
  • Docker环境安装

    安装Docker 开启Docker服务 查看安装结果 设置开机启动 配置Docker镜像下载加速
  • 【第42篇】MicroNet:以极低的 FLOP 实现图像识别

    文章目录 摘要 一 简介 二 相关工作 三 我们的方法 MicroNet 3 1 设计原则 3 2 Micro Factorized 卷积 3 3 动态 Shift Max 3 4 与先前工作的关系 四 MicroNet 架构 五 实验 I
  • JWT解析库-nimbus-jose-jwt

    JWT解析库 nimbus jose jwt nimbus jose jwt 使用他可以生成或者解析对称加密或者非对称加密的的JWT JWS是JWT规范的落地实现 1 依赖 1 1 pom依赖
  • 深夜好文

    题图 https unsplash com oldskool2016 无论项目中还是面试都离不开装饰器话题 装饰器的强大在于它能够在不修改原有业务逻辑的情况下对代码进行扩展 权限校验 用户认证 日志记录 性能测试 事务处理 缓存等都是装饰器