Python 装饰器基础

目录

  • 一、基本原理
  • 二、核心作用
  • 三、Demo示例
    • 示例1:基本装饰器
    • 示例2:带参数的装饰器
    • 示例3:多个装饰器
  • 四、正确使用装饰器
  • 五、pyparamvalidate 装饰器项目

装饰器(Decorators)是 Python 中一种强大而灵活的功能,它允许你在不修改原始函数代码的情况下,动态地修改或增强函数的行为。

一、基本原理

装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数。在这个过程中,可以在新函数中添加额外的功能,或者修改原始函数的行为。

二、核心作用

  1. 代码重用: 装饰器可以用于包装通用的功能,使其能够被多个函数共享。
  2. 代码增强: 通过装饰器在不改变原始函数结构的情况下,为其增加额外的功能。
  3. 面向切面编程: 装饰器允许在不修改函数代码的情况下,将横切关注点(如日志、性能监控)从业务逻辑中分离出来。

三、Demo示例

示例1:基本装饰器


def simple_decorator(func):def wrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")return wrapper@simple_decorator
def say_hello():print("Hello!")say_hello()

输出结果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

@decorator 语法糖是 Python 装饰器的简便写法,这段代码等效于:say_hello = simple_decorator(say_hello)

使用 @decorator 语法糖时,解释器会自动将下方的函数传递给装饰器,并将返回的结果重新赋值给原函数名。这样可以在不改变原函数调用方式的情况下应用装饰器。

示例2:带参数的装饰器


def decorator_with_args(arg):def actual_decorator(func):def wrapper(*args, **kwargs):print(f"Decorator argument: {arg}")for i in range(arg):func(*args, **kwargs)return wrapperreturn actual_decorator@decorator_with_args(3)
def greet(name):print(f"Hello, {name}!")greet("Alice")

输出结果:

Decorator argument: 3
Hello, Alice!
Hello, Alice!
Hello, Alice!

示例3:多个装饰器


def decorator1(func):def wrapper1():print("Decorator 1")func()return wrapper1def decorator2(func):def wrapper2():print("Decorator 2")func()return wrapper2@decorator1
@decorator2
def my_function():print("Original function")my_function()

输出结果:

Decorator 1
Decorator 2
Original function

执行顺序说明:

  • @decorator2my_function 上应用了 decorator2 装饰器。实际上,这相当于执行了 my_function = decorator2(my_function),将 my_function 替换为 decorator2 返回的新函数 wrapper2
  • 然后,@decorator1 在已经被 decorator2 装饰过的 my_function 上再次应用了 decorator1 装饰器。这相当于执行了 my_function = decorator1(decorator2(my_function)),将 decorator2 返回的函数 wrapper2替换为decorator1返回的新函数 wrapper1
  • 当调用 my_function() 时,实际上是调用了经过两个装饰器包装后的新函数。执行顺序是先执行最外层的 decorator1,再执行内层的 decorator2,最后执行原始函数。
    • 执行 decorator1 ,即执行 wrapper1(),"Decorator 1"被打印出来,然后执行 func()
    • 此时 funcwrapper2func() 相当于执行 wrapper2(), “Decorator 2” 被打印出来,然后执行 func()
    • 此时 funcmy_functionfunc() 相当于执行 my_function(),"Original function"被打印出来,结束。

四、正确使用装饰器

下面是一段问题代码:


def log_decorator(func):def wrapper(*args, **kwargs):print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}")result = func(*args, **kwargs)print(f"{func.__name__} returned: {result}")return resultreturn wrapper@log_decorator
def add(a, b):return a + b@log_decorator
def multiply(x, y):return x * yadd_result = add(2, 3)
print(f"add function name: {add.__name__}")  # 输出结果:add function name: wrapperprint(f"===================================")multiply_result = multiply(4, 5)
print(f"multiply function name: {multiply.__name__}")  # 输出结果:multiply function name: wrapper

输出结果:

Calling add with arguments (2, 3) and keyword arguments {}
add returned: 5
add function name: wrapper
===================================
Calling multiply with arguments (4, 5) and keyword arguments {}
multiply returned: 20
multiply function name: wrapper

从输出结果中,可以看出,add.__name__multiply.__name__ 均没有正确的显示函数名称。

为了解决这个问题,python 提供了内置的装饰器 @functools.wraps ,它的作用是在自定义装饰器中,复制原始函数的元信息到装饰器返回的新函数中,从而保留原函数的属性,包括:函数名称、文档字符串、参数信息等。

下面是正确使用装饰器的代码:


def log_decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}")result = func(*args, **kwargs)print(f"{func.__name__} returned: {result}")return resultreturn wrapper@log_decorator
def add(a, b):return a + b@log_decorator
def multiply(x, y):return x * yadd_result = add(2, 3)
print(f"add function name: {add.__name__}")  # 输出结果:add function name: addprint(f"===================================")multiply_result = multiply(4, 5)
print(f"multiply function name: {multiply.__name__}")  # 输出结果:multiply function name: multiply

输出结果:

Calling add with arguments (2, 3) and keyword arguments {}
add returned: 5
add function name: add
===================================
Calling multiply with arguments (4, 5) and keyword arguments {}
multiply returned: 20
multiply function name: multiply

五、pyparamvalidate 装饰器项目

pyparamvalidate 是一个简单易用的函数参数验证器。它提供了各种内置验证器,支持自定义验证规则,有助于 python 开发人员轻松进行函数参数验证,提高代码的健壮性和可维护性。

项目地址:github 。如果该项目对您有帮助,麻烦帮忙在 github 上点个小星星。

如果您想进一步深入了解装饰器的使用,请参考实现过程:pyparamvalidate 参数校验器,从编码到发布全过程 。

该项目最终的实现效果是一个ParameterValidator装饰器类,如下:

@ParameterValidator("description").is_string().is_not_empty()
@ParameterValidator("gender", "Invalid").is_allowed_value(["male", "female"], "Gender must be either 'male' or 'female'")
@ParameterValidator("age", param_rule_des="Age must be a positive number").is_int().is_positive()
@ParameterValidator("name").is_string(exception_msg="Name must be a string").is_not_empty()
def example_function(name, age, gender='male', **kwargs):description = kwargs.get("description")return name, age, gender, description

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排序算法-冒泡排序

一、算法介绍 冒泡排序是一种简单的排序算法,其核心思想是重复地遍历待排序列表,比较并交换相邻元素,使得较大的元素逐渐“冒泡”到列表的末尾,而较小的元素则逐渐上浮至列表的前端。该算法的名字源于类比元素的移动过程&#xff…

【Web】NSSCTF Round#16 Basic个人wp(全)

出题友好,适合手生复健。 目录 ①RCE但是没有完全RCE ②了解过PHP特性吗 ①RCE但是没有完全RCE 上来就是一段md5八股 (string)就是不让用数组了,然后强比较需要md5碰撞 ?md5_1%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc…

josef约瑟 中间继电器 HJDZ-E440额定电压:AC220V 卡轨安装

HJDZ-静态中间继电器 系列型号: HJDZ-A200静态中间继电器;HJDZ-A110静态中间继电器; HJDZ-A002静态中间继电器;HJDZ-A004静态中间继电器; HJDZ-E112静态中间继电器;HJDZ-E112L静态中间继电器&#xff1…

计算机网络——应用层(3)

计算机网络——应用层(3) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 点对点(P2P)P2P网络一般用途优点缺点总结 套接字编程基本步骤UDP套接字TCP套接字基本步骤 二者对比 小程一言 我的计算机网络专栏,是自…

无法创建链接服务器 “ERP“ 的 OLE DB 访问接口 “OraOLEDB.Oracle“ 的实例 (错误:7302)

在SQL SERVER 中,链接Oracle数据库我们通常会通过DB_LINK方式进行数据获取,交互,在创建链接服务器之前,需要安装Oracle客户端,附件为精简版Oracle驱动,安装完成后,会出现新的Oracle访问接口链接…

一个简单酒店住宿信息管理系统(C++)

住宿信息包括:住宿编号、姓名、身份证号码、联系电话、入住时间、房号、单价、入住接待人姓名、退房时间、退房接待人姓名、优惠费用、使用房内小物品费用、应付费用、备注。 本酒店住宿信息管理系统具有以下功能: (1)系统以菜单方式工作&am…

【数字图像处理】素描风格变换(一)—— 基本原理

在绘画风格中,素描风格是一种以黑白灰(或单色)色调为主的绘画风格,注重表现物体的轮廓、结构、明暗关系等细节。在数字图像处理中,素描风格变换是一种图像风格变换的过程,用于呈现素描的视觉效果。本文主要介绍数字图像素描风格变换的基本原理,以及使用 紫光同创 PGL22G…

Flutter--常用技术文档

配置 清华大学flutter镜像 export PUB_HOSTED_URLhttps://mirrors.tuna.tsinghua.edu.cn/dart-pub export FLUTTER_STORAGE_BASE_URLhttps://mirrors.tuna.tsinghua.edu.cn/flutter 社区镜象 export PUB_HOSTED_URLhttps://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_UR…

开放式耳机和骨传导耳机有什么区别?谁更值得入手?

很多人都知道开放式耳机,但很少有人知道其实骨传导耳机也算开放式耳机的一种,事实上,开放式耳机可以分为气传导耳机和骨传导耳机两种类型,这两种耳机佩戴方式都是开放双耳机,但传声方式有所不同,下面简单科…

《C语言学习》---郝斌版---笔记

简介 学习计算机,离不开C语言的学习,而C语言学习过程中的视频课教程,目前来说,如果郝斌老师的C语言排第二,没有人敢排第一 郝斌老师的C语言教程,通俗易懂,引人发思,特别适合新手入门…

如何定义核心场景用例?

首先我们解决两个问题: 1.什么是场景测试?2.什么是核心场景? 1、什么是场景测试? 🎯 1.1:什么是场景 事件触发时的情景形成了场景。场景必不可少的几个要素:环境、人、时间、行为。简而言之&…

Rust-变量

Rust的变量必须先声明后使用。对于局部变量,最常见的声明语法为: let variable:i32 100;与传统的C/C语言相比,Rust的变量声明语法不同。这样设计主要有以下几个方面的考虑。 语法分析更容易 从语法分析的角度来说,Rust的变量声明…

Linux系统编程(十一):高级 IO

参考引用 UNIX 环境高级编程 (第3版)嵌入式Linux C应用编程-正点原子 1. 非阻塞 I/O 阻塞就是进入了休眠状态,交出了 CPU 控制权阻塞 I/O 就是对文件的 I/O 操作(读写操作)是阻塞式的,非阻塞 I/O 就是对文件的 I/O 操作是非阻塞的…

MySQL 语句|使用MySQL中的GROUP BY获取每个组的最大和最小值记录

在 MySQL 中,可以使用 GROUP BY 语句结合聚合函数和子查询来获取每个组中的最大和最小值对应的记录 举个通用的例子 假设有一个名为 your_table 的表,其中包含两列:group_column 和 value_column 要分别获取每个组中的最大和最小值对应的记…

SpringBoot:泛型对象存取与转换<JedisPool>

操作redis时候泛型、对象操作的比较频繁不可能都转成字符串然后再查询转换比较复杂了&#xff0c;这个时候我们需要对存取转换进行封装便于快速开发 1、导入依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>&…

网络技术基础入门全套实验-厦门微思网络CCNA实验手册

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 微思简介&#xff08;https://www.xmws.cn) 微思成立于2002年&#xff0c;是一个诚信敬业、积极向上、充满活力、专注技术服务的企业。 微思获得了八…

为什么修改了.gitignore文件不生效,Git常见问题解决

Git 是基于 Linux内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,一般不需要服务器端软件支持(注:这得分是用什么样的服务端,使用http协议或者git协议等不太一样。并且在push和pull的时候和服务器端还是有交互的。),…

电子学会2023年12月青少年软件编程(图形化)等级考试试卷(四级)真题,含答案解析

青少年软件编程(图形化)等级考试试卷(四级) 分数:100 题数:24 一、单选题(共10题,共30分) 1. 运行下列程序,输入“abcdef”,程序结束后,变量“字符串”是?( )

Linux--部署 Tomcat 及其负载均衡

1.案例前置知识点 1&#xff09;Tomcat简介 名称由来&#xff1a;Tomcat最初是由 Sun的软件构架师詹姆斯邓肯戴维森开发的。后来他帮助将其变 为开源项目&#xff0c;并由Sun贡献给Apache软件基金会。由于大部分开源项目OReilly都会出一本相关的 书&#xff0c;并且将其封面设…

SpringMVC视图

文章目录 1、ThymeleafView2、转发视图3、重定向视图4、视图控制器view-controller5、总结 SpringMVC中的视图是View接口&#xff0c;视图的作用渲染数据&#xff0c;将模型Model中的数据展示给用户SpringMVC视图的种类很多&#xff0c;默认有转发视图和重定向视图当工程引入js…