【Python】装饰器函数

专栏文章索引:Python

原文章:装饰器函数基础_装饰函数-CSDN博客

目录

1. 学习装饰器的基础

2.最简单的装饰器

3.闭包函数装饰器

4.装饰器将传入的函数中的值大写

5. 装饰器的好处

6. 多个装饰器的执行顺序

7. 装饰器传递参数

8. 结语


1. 学习装饰器的基础

学习装饰器之前, 先需要明白函数的一些特性 :

  1. 函数也是对象, 可以将函数分配给变量, 也可以作为参数传递给另外一个函数,甚至可以储存在数据结构中。
  2. 函数内部也可以定义函数, 我们常常称这种函数为闭包函数, 其可以使用父函数的局部状态(变量)而非全局状态(变量)
    总之 ,python中一切皆对象, 不要把函数想的太高深。

概述:装饰器是可调用的, 将可调用的对象作为输入传入装饰器函数并返回另一个可调用对象。

2.最简单的装饰器

def hello(func):
    return func


def greet():
    return "Hello World"


greet = hello(greet)
print(greet()) # Hello World

我们定义了一个hello()函数, 它接受一个参数func, 其实在这里的func接受的是一个函数的内存地址, 这个函数就是后面定义的greet(), 它返回一句话, 将greet作为参数传给hello函数, 并接受返回值,这里需要注意的是, 返回值func在接受了greet()函数的内存地址后, 如果直接打印, 打印的会是一串内存地址

def hello(func):
    return func


def greet():
    return "Hello World"


greet = hello(greet)
print(greet) # <function greet at 0x02B27AE0>

因为在python中, 定义一个函数,相当于在内存空间开辟一块用于存放函数内容的空间 ,也就是我们案例中传给hello函数的值greet其实就是一块内存空间, 将它传给了func,这个时候内存空间的指向发生了变化, 不在执行greet函数而是赋值给了func。

不过这样做确实麻烦, 既然是装饰器, 装饰二字何来?,而且多层的函数调用嵌套容易发生错误, 所以python引用了@语法糖的形式, 使用@语法会立即修饰该函数,给它穿上一套准备好的衣服。修改一下代码:

def hello(func):
    return func

@hello
def greet():
    return "Hello World"

print(greet())

我们来理一下程序行走的流程, 经过hello函数, 不会立即执行, 预留一片储存空间, 存放返回的func, 接下来走到了@hello这里, 实际上就是greet = hello(greet),将greet作为参数传入hello函数, 并保留返回的结果func, 等待调用, 这个时候greet地址已经在hello函数中了,就等着调用了 ,所以最后执行调用, 程序进入func内存空间, 调用func地址中的内容, 也就是调用greet函数。

说到这里可能还是有点迷, 下面在举一个难点的例子, 用到了函数嵌套,闭包函数, 这也是常见的装饰器函数:

3.闭包函数装饰器

import time

def timer(func):
    def wapper(*args, **kwargs):
        print("开始计时")
        start_time = time.time()
        func()
        print('执行吗??')
        end_time = time.time()
        print('耗时: ', end_time - start_time)
    return wapper

@timer
def run():
    print('To do the job')
    time.sleep(2)
    print('End the job')
run()

咋一看挺复杂, 下面来理一理。
程序走到了@timer这里 ,实际上就是 run = timer(run), 在这里是将run作为参数传递给func, 相当于一个载体, 承载着run进入wrapper函数, 此时程序并不执行, 还没有最终的调用,只是返回一个wrapper地址,紧接着执行run函数,最后调用run(),注意:这时候直接执行run()函数, 也就是直接执行刚刚返回的wrapper内存地址,打印 开始计时, 这时候因为函数内部调用了func(),所以执行run函数,静止2秒, 打印最后一条数据, 其实就是func代替、等于run, 执行run函数里面的命令。

总结: 最终的函数调用时, 先进入装饰器函数, 顺次执行, 遇到传入的函数地址进行调用, 在依次执行
运行结果:

开始计时
To do the job
End the job
执行吗??
耗时:  2.0005037784576416

4.装饰器将传入的函数中的值大写

# 这是一个装饰器函数
def upperfunc(func):
    def wrapper():
        original_result = func() # 接受func()函数的返回值, 也就是要变为大写的值
        transform_result = original_result.upper()
        return transform_result
    return wrapper


@upperfunc
def greet():
    return "hello world"


print(greet())

# HELLO WORLD

同样的, 大体流程是 返回一个wrpper地址空间等待执行, 装饰器装饰greet函数,
使其中的返回值大写, 最后调用greet函数,首先执行装饰器函数中的地址空间, 在空间中将hello world大写, 并返回结果。

5. 装饰器的好处

说到这里该说一说装饰器的好处, 就像上面这个例子, 假设有一个你在程序中遇到一个问题, 需要将一些字符串大写, 而恰巧这些字符串数量不少, 一个一个调用upper方法是不是感觉头痛, 这时使用装饰器不啻为一个好的选择。


6. 多个装饰器的执行顺序

 如果我们想要定义多个装饰器且将这些装饰器装饰于一个函数, 那么装饰器的执行顺序是必须要清楚的,下面定义两个装饰器, 分别添加一些HTML标签, 由此观察装饰器的执行顺序

# div 标签包围
def div_tags(func):
    def wrapper():
        return '<div>' + func() + '</div>'
    return wrapper


#  a 标签包围
def a_tags(func):
    def wrapper():
        return '<a>' + func() + '</a>'
    return wrapper


@a_tags
@div_tags
def greet():
    return "hello world"


print(greet())

结果:

<a><div>hello world</div></a>

从结果可以看出多个装饰器是从下向上执行的,
回到上面说的装饰器的好处, 我们来看一下不用装饰器执行是怎么调用的:

decorated_greet = div_tas(a_tags(greet))
print(decorated_greet())

执行结果是一样的, 这样写不仅容易发生错误, 而且多重的嵌套很难让人提起兴趣去阅读代码是怎样执行的。


7. 装饰器传递参数

 上面讲的都是一些简单无参的函数, 下面来介绍一下有参数传递的装饰器函数 ,在这里就需要用到 * 操作符了, 它会接受一个或多个位置参数, 而** 操作符接受关键字参数
如果这里不懂的可以百度一下, 应该都有

# 装饰器传递参数
def Prinname(func):
    def wapper(*args, **kwargs):
        func(**kwargs)
    return wapper

@Prinname
def run(name):
    print('姓名:', name)


def main():
    run(name='bai')


if __name__ == '__main__':
    main()

在这里闭包函数中有两个形参, 他们基本上可以接受任何传入的参数, 所有下面run函数传入了关键字参数,类似于key1=value1, key2=value2, 只不过定义了个函数入口, 从这里开始执行,大体原理没变。

结果:

姓名: bai


8. 结语

以上是装饰器函数的一些基本内容, 要尝试着敲一遍才能理解, 如果第一遍看不懂的话,不要慌, 因为装饰器,闭包函数,*操作符等算是python中的中高级内容了, 我也是学习了好多遍,虽然实际中从没用过,但是学学对于理解pyhton的特性还是很有帮助的, 最后, 希望这篇文章可以帮到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/444322.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++中string的模拟实现(超详细!!!)

1.string的成员变量、&#xff08;拷贝&#xff09;构造、析构函数 1.1.成员变量 private:char* _str;size_t _size; //string中有效字符个数size_t _capacity; //string中能存储有效字符个数的大小 1.2&#xff08;拷贝&#xff09;构造函数 //构造函数string(const char* …

Chain of Verification(验证链、CoVe)—理解与实现

原文地址&#xff1a;Chain of Verification (CoVe) — Understanding & Implementation 2023 年 10 月 9 日 GitHub 存储库 介绍 在处理大型语言模型&#xff08;LLM&#xff09;时&#xff0c;一个重大挑战&#xff0c;特别是在事实问答中&#xff0c;是幻觉问题。当答案…

React-路由导航

1.声明式路由导航 1.1概念 说明&#xff1a;声明式导航是指通过在模版中通过<Link/>组件描述出要跳转到哪里去&#xff0c;比如后台管理系统的左侧菜单通常使用这种方式进行。 import {Link} from "react-router-dom" const Login()>{return (<div>…

资源哟正版无授权模版源码(含搭建教程)

资源哟 v1.3 – 新增两种首页布局 – 新增幻灯片插件 – 优化深色模式颜色效果 – 优化导航页面左侧栏目跳转效果 – 优化后台辅助插件当前页面打开 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/88898100 更多资源下载&#xff1a;关注我。

Linux多线程之线程控制

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 一、pthread_crate 二、pthread_join 三、p…

RAG、数据隐私、攻击方法和安全提示

原文地址&#xff1a;RAG, Data Privacy, Attack Methods & Safe-Prompts 最近的一项研究探讨了 RAG 安全漏洞以及通过检索数据集访问私有数据的方式。还讨论了防御和安全提示工程示例。 介绍 RAG 在构建生成式 AI 应用程序中非常受欢迎。RAG 在生成式 AI 应用中采用的原因…

Elasticsearch架构原理

一. Elasticsearch架构原理 1、Elasticsearch的节点类型 在Elasticsearch主要分成两类节点&#xff0c;一类是Master&#xff0c;一类是DataNode。 1.1 Master节点 在Elasticsearch启动时&#xff0c;会选举出来一个Master节点。当某个节点启动后&#xff0c;然后使用Zen D…

指针数组和数组指针(详细解释)

指针数组 指针数组的作用 指针数组和数组指针是C语言中常用的概念&#xff0c;它们分别有不同的作用和用法。 指针数组&#xff1a; 指针数组是一个数组&#xff0c;其中的每个元素都是指针类型。它可以用来存储多个指针&#xff0c;每个指针可以指向不同的数据类型或者相同…

Pytorch学习 day08(最大池化层、非线性激活层、正则化层、循环层、Transformer层、线性层、Dropout层)

最大池化层 最大池化&#xff0c;也叫上采样&#xff0c;是池化核在输入图像上不断移动&#xff0c;并取对应区域中的最大值&#xff0c;目的是&#xff1a;在保留输入特征的同时&#xff0c;减小输入数据量&#xff0c;加快训练。参数设置如下&#xff1a; kernel_size&#…

类与对象-对象特性

师从黑马程序员 对象的初始化和清理 构造函数和析构函数 用于完成对象的初始化和清理工作 如果我们不提供构造和析构&#xff0c;编译器会提供编译器提供的构造函数和析构函数是空实现 构造函数&#xff1a;主要用于创建对象时为对象的成员属性赋值&#xff0c;构造函数由编…

了解华为(PVID VLAN)与思科的(Native VLAN)本征VLAN的区别并学习思科网络中二层交换机的三层结构局域网VLAN配置

一、什么是二层交换机&#xff1f; 二层交换机&#xff08;Layer 2 Switch&#xff09;是一种网络设备&#xff0c;主要工作在OSI模型的数据链路层&#xff08;第二层&#xff09;&#xff0c;用于在局域网内部进行数据包的交换和转发。二层交换机通过学习MAC地址表&#xff0…

毅速3D打印随形透气钢:模具困气排气革新之选

在注塑生产过程中&#xff0c;模具内的气体若无法有效排出&#xff0c;往往会引发困气现象&#xff0c;导致产品表面出现气泡、烧焦等瑕疵。这些瑕疵不仅影响产品的美观度&#xff0c;更可能对其性能造成严重影响&#xff0c;甚至导致产品报废&#xff0c;从而增加生产成本。 传…

政安晨:【深度学习处理实践】(四)—— 实施一个温度预测示例

在开始使用像黑盒子一样的深度学习模型解决温度预测问题之前&#xff0c;我们先尝试一种基于常识的简单方法。 它可以作为一种合理性检查&#xff0c;还可以建立一个基准&#xff0c;更高级的机器学习模型需要超越这个基准才能证明其有效性。对于一个尚没有已知解决方案的新问…

Linux之生产消费者模型

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 我们在条件满足的时候&#xff0c;唤醒指定的线程&a…

超越Chain-of-Thought LLM 推理

原文地址&#xff1a;Beyond Chain-of-Thought LLM Reasoning 2024 年 2 月 13 日 介绍 最近的一项研究解决了需要增强大型语言模型 (LLM) 的推理能力&#xff0c;超越直接推理 (Direct Reasoning&#xff0c;DR) 框架&#xff0c;例如思想链和自我一致性&#xff0c;这些框架可…

分割模型TransNetR的pytorch代码学习笔记

这个模型在U-net的基础上融合了Transformer模块和残差网络的原理。 论文地址&#xff1a;https://arxiv.org/pdf/2303.07428.pdf 具体的网络结构如下&#xff1a; 网络的原理还是比较简单的&#xff0c; 编码分支用的是预训练的resnet模块&#xff0c;解码分支则重新设计了。…

HTML入门:属性

你好&#xff0c;我是云桃桃。今天来聊一聊 HTML 属性写法和特点。 HTML 属性是用于向 HTML 标签&#xff08;也叫 HTML 元素&#xff09;提供附加信息或配置的特性。 如果说&#xff0c;把HTML 标签比作一个房子&#xff0c;HTML 标签定义了房子的结构和用途&#xff0c;比如…

基于SpringBoot的闲置房屋搜索平台设计与实现

目 录 摘 要 I Abstract II 引 言 1 1相关技术 3 1.1 jQuery技术简介 3 1.2 SpringBoot框架简介 3 1.3 Bootstrap框架简介 4 1.4 ECharts框架简介 4 1.5 百度地图API简介 4 1.6 Ajax技术简介 5 1.7 MySQL数据库简介 5 1.8本章小结 6 2系统分析 7 2.1功能需求 7 2.2非功能需求 …

微软财务GPT Excel Copilot for Finance使用攻略

功能本身不收费&#xff0c;但是这个功能需要微软的商业版office账号才能使用&#xff0c;如果你没有账号&#xff0c;可以直说。 在桌面Excel软件中登录账号后&#xff0c;点击“copilot for finance”按钮&#xff0c;如果没有出现&#xff0c;则点击“加载项”&#xff0c;…

2024 年中国高校大数据挑战赛赛题 D:行业职业技术培训能力评价完整思路以及源代码分享

中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴 熟的技术工人进入工厂。某行业在全国有多所不同类型&#xff08;如国家级、 省级等&#xff09;的职业技术培训学校&#xff0c;进行 5 种技能培训。学员入校时需要 进行统一的技能考核&#xf…