Python装饰器的通俗理解

通俗理解Python装饰器 深入理解并应用Python装饰器 阅读详情

转载 原出处:http://blog.youkuaiyun.com/u013471155
在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Python装饰器的理解更加的透彻。在文中如有遗漏和不足,欢迎交流和指点。

很多人对装饰器难以理解,原因是由于以下三点内容没有搞清楚:

  1. 关于函数“变量”(或“变量”函数)的理解
  2. 关于高阶函数的理解
  3. 关于嵌套函数的理解

那么如果能对以上的问题一一攻破,同时遵循装饰器的基本原则,相信会对装饰器有个很好的理解的。那么我们先来看以下装饰器的目的及其原则。

1、装饰器

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式
  3. 满足1、2的情况下给程序增添功能

那么根据需求,同时满足了这三点原则,这才是我们的目的。因为,下面我们从解决这三点原则入手来理解装饰器。

等等,我要在需求之前先说装饰器的原则组成:

< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

这个式子是贯穿装饰器的灵魂所在!

2、需求的实现

假设有代码:

improt time
def test():
    time.sleep(2)
    print("test is running!")
test()

很显然,这段代码运行的结果一定是:等待约2秒后,输出

test is running

那么要求在满足三原则的基础上,给程序添加统计运行时间(2 second)功能
在行动之前,我们先来看一下文章开头提到的原因1(关于函数“变量”(或“变量”函数)的理解)

2.1、函数“变量”(或“变量”函数)

假设有代码:

x = 1
y = x
def test1():
    print("Do something")
test2 = lambda x:x*2

那么在内存中,应该是这样的:

这里写图片描述
很显然,函数和变量是一样的,都是“一个名字对应内存地址中的一些内容”
那么根据这样的原则,我们就可以理解两个事情:

  1. test1表示的是函数的内存地址
  2. test1()就是调用对在test1这个地址的内容,即函数

如果这两个问题可以理解,那么我们就可以进入到下一个原因(关于高阶函数的理解)

2.2高阶函数

那么对于高阶函数的形式可以有两种:

  1. 把一个函数名当作实参传给另外一个函数(“实参高阶函数”)
  2. 返回值中包含函数名(“返回值高阶函数”)

那么这里面所说的函数名,实际上就是函数的地址,也可以认为是函数的一个标签而已,并不是调用,是个名词。如果可以把函数名当做实参,那么也就是说可以把函数传递到另一个函数,然后在另一个函数里面做一些操作,根据这些分析来看,这岂不是满足了装饰器三原则中的第一条,即不修改源代码而增加功能。那我们看来一下具体的做法:

还是针对上面那段代码:

improt time

def test():
    time.sleep(2)
    print("test is running!")

def deco(func):  
    start = time.time()
    func() #2
    stop = time.time()
    print(stop-start)

deco(test) #1

我们来看一下这段代码,在#1处,我们把test当作实参传递给形参func,即func=test。注意,这里传递的是地址,也就是此时func也指向了之前test所定义的那个函数体,可以说在deco()内部,func就是test。在#2处,把函数名后面加上括号,就是对函数的调用(执行它)。因此,这段代码运行结果是:

test is running! 
the run time is 3.0009405612945557

我们看到似乎是达到了需求,即执行了源程序,同时也附加了计时功能,但是这只满足了原则1(不能修改被装饰的函数的源代码),但这修改了调用方式。假设不修改调用方式,那么在这样的程序中,被装饰函数就无法传递到另一个装饰函数中去。

那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名

如下代码:

improt time

def test():
    time.sleep(2)
    print("test is running!")

def deco(func):  
    print(func)
    return func 
t = deco(test) #3
#t()#4

test()

我们看这段代码,在#3处,将test传入deco(),在deco()里面操作之后,最后返回了func,并赋值给t。因此这里test => func => t,都是一样的函数体。最后在#4处保留了原来的函数调用方式。
看到这里显然会有些困惑,我们的需求不是要计算函数的运行时间么,怎么改成输出函数地址了。是因为,单独采用第二张高阶函数(返回值中包含函数名)的方式,并且保留原函数调用方式,是无法计时的。如果在deco()里计时,显然会执行一次,而外面已经调用了test(),会重复执行。这里只是为了说明第二种高阶函数的思想,下面才真的进入重头戏。

2.3 嵌套函数

嵌套函数指的是在函数内部定义一个函数,而不是调用,如:

def func1():
    def func2():
        pass
而不是
def func1():
    func2()

另外还有一个题外话,函数只能调用和它同级别以及上级的变量或函数。也就是说:里面的能调用和它缩进一样的和他外部的,而内部的是无法调用的。

那么我们再回到我们之前的那个需求,想要统计程序运行时间,并且满足三原则。

代码:

improt time

def timer(func) #5
    def deco():  
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco

test = timer(test) #6

def test():
    time.sleep(2)
    print("test is running!")   
test() #7

这段代码可能会有些困惑,怎么忽然多了这么多,暂且先接受它,分析一下再来说为什么是这样。

首先,在#6处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。

然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。那么在#7处调用的实际上是deco()。

那么这段代码在本质上是修改了调用函数,但在表面上并未修改调用方式,而且实现了附加功能。

那么通俗一点的理解就是:

把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)

这样做的原因是:

我们要保留test(),还要统计时间,而test()只能调用一次(调用两次运行结果会改变,不满足),再根据函数即“变量”,那么就可以通过函数的方式来回闭包。于是乎,就想到了,把test传递到某个函数,而这个函数内恰巧内嵌了一个内函数,再根据内嵌函数的作用域(可以访问同级及以上,内嵌函数可以访问外部参数),把test包在这个内函数当中,一起返回,最后调用这个返回的函数。而test传递进入之后,再被包裹出来,显然test函数没有弄丢(在包裹里),那么外面剩下的这个test标签正好可以替代这个包裹(内含test())。
这里写图片描述
至此,一切皆合,大功告成,单只差一步。

3、 真正的装饰器

根据以上分析,装饰器在装饰时,需要在每个函数前面加上:

test = timer(test)

显然有些麻烦,Python提供了一种语法糖,即:

@timer

这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。
以上为无参形式

4、装饰有参函数

improt time

def timer(func)
    def deco():  
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
test() 

对于一个实际问题,往往是有参数的,如果要在#8处,给被修饰函数加上参数,显然这段程序会报错的。错误原因是test()在调用的时候缺少了一个位置参数的。而我们知道test = func = deco,因此test()=func()=deco()
,那么当test(parameter)有参数时,就必须给func()和deco()也加上参数,为了使程序更加有扩展性,因此在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。

完整代码如下:

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
test() 

那么我们再考虑个问题,如果原函数test()的结果有返回值呢?比如:

def test(parameter): 
    time.sleep(2)
    print("test is running!")   
    return "Returned value"

那么面对这样的函数,如果用上面的代码来装饰,最后一行的test()实际上调用的是deco()。有人可能会问,func()不就是test()么,怎么没返回值呢?

其实是有返回值的,但是返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)#9
        stop = time.time()
        print(stop-start)
        return res#10
    return deco

其中,#9的值在#10处返回。

完整程序为:

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res 
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
    return "Returned value"
test() 

5、带参数的装饰器

又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:

@decorator(parameter = value)

比如有两个函数:

def task1():
    time.sleep(2)
    print("in the task1")

def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

要对这两个函数分别统计运行时间,但是要求统计之后输出:

the task1/task2 run time is : 2.00……

于是就要构造一个装饰器timer,并且需要告诉装饰器哪个是task1,哪个是task2,也就是要这样:

@timer(parameter='task1') #
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2') #
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

那么方法有了,但是我们需要考虑如何把这个parameter参数传递到装饰器中,我们以往的装饰器,都是传递函数名字进去,而这次,多了一个参数,要怎么做呢?
于是,就想到再加一层函数来接受参数,根据嵌套函数的概念,要想执行内函数,就要先执行外函数,才能调用到内函数,那么就有:

def timer(parameter): #
    print("in the auth :", parameter)

    def outer_deco(func): #
        print("in the outer_wrapper:", parameter)
        def deco(*args, **kwargs):
        return deco
    return outer_deco

首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:

timer = timer(parameter)
task1 = timer(task1)

后面的运行就和一般的装饰器一样了:

import time

def timer(parameter):

    def outer_wrapper(func):

        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task1 run time is :", stop - start)
            elif parameter == 'task2':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task2 run time is :", stop - start)

        return wrapper

    return outer_wrapper

@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

至此,装饰器的全部内容结束。
原创地址://blog.youkuaiyun.com/u013471155/article/details/68960244

逆向分析美团外卖mtgsig3.0签名算法:从抓包到算法还原实战 在移动应用开发与安全领域,API接口签名是保障数据传输完整性与身份验证的核心机制。其基本原理是通过特定的算法,将请求参数、时间戳等数据与密钥结合,生成唯一的签名串,服务器通过验证签名来识别请求的合法性。这项技术的核心价值在于防止数据篡改、重放攻击和未授权访问,是构建安全客户端-服务器通信的基石。在实际应用中,签名算法广泛应用于电商、社交、金融等各类App的接口保护场景。本文以美团外卖App的mtgsig3.0签名为具体案例,详细拆解了其生成逻辑。通过结合Frida动态调试与Jadx静态分析,文章深入探讨了如 阅读详情

相关推荐

嵌入式C开发必备:用Cursor规则搞定代码风格检查(附Pikachu项目实战)

本文以Pikachu开源项目为例,详细介绍了如何利用Cursor的AI代理规则系统,为嵌入式C开发项目配置一套完整的代码风格检查与规范执行方案。通过实战演示,指导开发者创建项目级规则,实现命名、格式、硬件抽象层及驱动开发的自动化规范,从而提升代码一致性、可维护性和团队协作效率。

weixin_29181861的博客 598

关于es7的装饰器

参考博文地址:https://segmentfault.com/a/1190000014495089 一,前言        从ES6开始,js有了类的定义和操作(像class、extends),我们又希望在多个不同类之间共享或者扩展一些方法或行为,js参考了python,在ES7的语法中增加了装饰器 二,python的装饰器 &nbsp...

weixin_41320468的博客 616

FPGA实战:MIG IP核AXI接口DDR3读写优化指南

本文详细解析了FPGA中MIG IP核AXI接口的DDR3读写优化策略,涵盖关键参数配置、突发传输技巧及时序收敛方案。通过AXI接口优化和突发长度选择,可实现高达12.8GB/s的理论带宽,提升DDR3访问效率。文章还提供了Vivado配置实例和调试技巧,助力开发者解决FPGA内存控制中的实际问题。

weixin_29168393的博客 546

问题解决:Python代码连续运行两次

背景:今天准备开始利用Pycharm学习numpy有关内容,在进行第一步时发现程序总是会自动运行两遍。解决方案:将numpy文件改为其他的。这样表示,以后文件的命名要避免和其他包的名字相同。原因:py文件命名为了numpy。

m0_67504012的博客 1103

Win10 + Linux 双系统删除(Linux)

删除Linux所使用的磁盘分区 进入此电脑->计算机->管理->磁盘管理

weixin_52247624的博客 943

Python画常见分布的概率密度图

因为做概随大作业,了解了一些python画概率分布图的方法。

qq_44807946的博客 5204

装饰器(Decorator)初步理解

装饰器(Decorator)作为Python中极具特色且强大的语言特性,近年来在软件开发领域得到广泛应用。它通过简洁的语法,优雅地实现函数或类的行为增强,成为提升代码复用性、解耦性和可维护性的利器。

测试者家园 510

运行codex时出现登录失败:failed to start login server: 以一种访问权限不允许的方式做了一个访问套接字的尝试。 (os error 10013) 解决方案

本文主要介绍了运行codex时出现登录失败:failed to start login server: 以一种访问权限不允许的方式做了一个访问套接字的尝试。 (os error 10013) 解决方案,希望能对使用xx的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 10万+

如何通俗易懂地理解Python装饰器?怎么使用?

就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。装饰器的功能特点: 代码说明:执行结果: 3. 装饰器的语法糖写法 如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰 说明:执行结果: 二、装饰器的使用 1. 装饰器的使用场景 函数执行时间的统

黑马程序员官方博客 522

第32篇:Vibe Coding时代:LangGraph + Redis 任务状态持久化实战,解决服务重启后任务状态丢失问题

LangGraph + Redis任务状态持久化方案 本文针对Agent任务状态丢失问题,提出基于Redis的持久化解决方案。当服务重启时,传统内存存储方式会导致任务状态丢失,影响用户体验和运维排查。 核心方案 使用Redis Hash存储任务状态,key格式为agent:task:{task_id} 设计完整状态字段包括task_id、status、progress等关键信息 实现task_store模块提供CRUD操作接口 在Agent执行各节点更新Redis中的任务状态 技术实现 通过Celery执行

sundong_81的博客 42

SAP_FI模块_固定资产期初导入全流程(含导入代码)

SAP 期初导入资产主数据以及金额相关的操作全流程记录,包含了批导程序的代码,以及错误记录

风沙的博客 1404

装饰器python的通俗理解_Python装饰器的通俗理解

转载:http://blog.youkuaiyun.com/u013471155在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Python装饰器的理解更加的透彻。在文中如有遗漏和不足,欢迎交流和指点。很多人对装饰器难以理解,原因是由于以下三点内容没有搞清楚:关于...

weixin_39612038的博客 78

训练YOLOv11 | Pytorch和Pycharm的安装教程适用W11系统(附详细的系统变量添加过程)

根据你之前下载的版本选择对应的选项即可,然后复制底下的绿色代码到Anaconda的命令行输入即可(这里要在你安装的python环境里进行安装,例如我的环境名字是PyTorch,那么就是activate PyTorch里进行安装)然后同之前一样会加载一会,然后会询问我们是否下载配件,我们输入Y等待即可(下载过程中我们要确保网络通常如果有波动可能会下载失败)。类似于我的电脑是Windows11选择上面红框内的选项即可点击后即下载,(注意这里下载的是zip文件)下载完成后复制下面几个文件。

Snu77的博客 1万+

python装饰器的通俗理解_python装饰器的通俗理解

在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Python装饰器的理解更加的透彻。在文中如有遗漏和不足,欢迎交流和指点。允许转载并注明出处:http://blog.youkuaiyun.com/u013471155很多人对装饰器难以理解,原因是由于以下三点内容没...

weixin_39914975的博客 112

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

怎么理解python中的装饰器(小白通俗理解)?

怎么理解python中的装饰器(小白通俗理解) 今天学习了python中的装饰器,我来说一说我对python装饰器的理解。 作用 它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能。 用途 应用于缓存、 权限校验(如django中的@login_required和@permission_required装饰器) 性能测试(比如统计一段程序的运行时间) 插入日志等应用场景 python...

范范范国超的博客 456

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

装饰器python的通俗理解_通俗讲解python 装饰器

装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。其实在平常写写脚本的过程中,这个知识点你可能用到不多但在面试的时候,这可是一个高频问题。一、什么是装饰器所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。放心,绝对不是"Hello World"!def hello():print("你...

weixin_39988779的博客 103

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

python中装饰器的通俗解释_python装饰器的通俗理解

在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过思考和查阅才能略有领悟,我希望以下的内容会对你有帮助,我也努力通过通俗的方式使得对Python装饰器的理解更加的透彻。在文中如有遗漏和不足,欢迎交流和指点。允许转载并注明出处:http://blog.youkuaiyun.com/u013471155很多人对装饰器难以理解,原因是由于以下三点内容没...

weixin_39799561的博客 126

装饰器python的通俗理解_理解 Python 装饰器就看这一篇

讲 Python 装饰器前,我想先举个例子,跟装饰器这个话题可能很贴切。每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了。于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套...

weixin_39883065的博客 867

装饰器python的通俗理解_如何更通俗地讲解Python的装饰器?

我从以下几点,由浅入深详细讲解一下Python装饰器:什么是装饰器?为什么用装饰器?在哪里用装饰器?然后以示例+讲解相结合的方式阐述,同时会讲解一些在很多教程和书籍中不会涉及到的内容。什么是Python装饰器?顾名思义,从字面意思就可以理解,它是用来"装饰"Python的工具,使得代码更具有Python简洁的风格。换句话说,它是一种函数的函数,因为装饰器传入的参数就是一个函数,然后通过实现各种功能...

weixin_39608613的博客 341

Python装饰器通俗理解

Python装饰器简单来讲就是用于扩展函数功能的一种工具,在扩展功能方面其与类的继承有些相似,都是为了提高代码的复用性。举个例子就是把孙悟空塞入炼丹炉,然后就出来一个还是会吃桃子会耍金箍棒,但是有火眼金睛的猴哥,只是多了特效技能,这就是为什么叫做装饰器的原因。与类的继承相比,其用途有所区别,并且降低了代码的耦合性(即类与类之间乱七八糟的关系导致整个代码体系臃肿的情形)。Python装饰器的应用场景有很多,其中一个很普遍的功能就是打印日志或者运行时间。{}{}{}......

JohnJim的博客 1389
上一篇: Python3 学习笔记24_函数式编程-装饰器_20180314
下一篇: Python3 学习笔记25_函数式编程-偏函数_20180315
metears
博客等级 码龄19年 15粉丝 33原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值